#javascript #syntax #ecmascript-6 #babeljs
#javascript #синтаксис #ecmascript-6 #babeljs
Вопрос:
У меня чрезвычайно маленький код ES6:
var name = "MyName";
var version = "1.2.3";
var theThing = {
name,
version,
run: () => {
console.log(`You're using ${this.name}@${this.version}`);
}
};
theThing.run();
Когда я запускаю его в консоли браузера (chrome 53), я получаю ожидаемый результат: You're using MyName@1.2.3
записывается в консоль. Обе строки шаблона работают вместе с сокращенным синтаксисом объектного литерала.
Однако, когда я пытаюсь использовать gulp / babel для переноса этого кода в ES5, используя самую базовую настройку (взятую из здесь):
const gulp = require('gulp');
const babel = require('gulp-babel');
gulp.task('default', () => {
return gulp.src('src/app.js')
.pipe(babel({
presets: ['es2015']
}))
.pipe(gulp.dest('dist'));
});
Я получаю следующий вывод:
"use strict";
var name = "MyName";
var version = "1.2.3";
var theThing = {
name: name,
version: version,
run: function run() {
console.log("You're using " undefined.name "@" undefined.version);
}
};
theThing.run();
Как вы можете видеть, это вызывается undefined.name
вместо this.name
, я понятия не имею, почему this
заменяется на undefined
. Конечно, этот код работает не так, как ожидалось:
VM329:8 Uncaught TypeError: Cannot read property 'name' of undefined(…)
и не совместим с ES6 (оригинальная реализация ES6 в chrome53 работает корректно).
Вы также можете увидеть эту проблему в {
console.log(`You’re using ${this.name}@${this.version}`);
}
};
theThing.run();» rel=»nofollow»>Babel REPL.
Я делаю что-то неправильно — или это ошибка в babel?
Ответ №1:
в вашем исходном коде попробуйте заменить
var theThing = {
name,
version,
run: () => {
console.log(`You're using ${this.name}@${this.version}`);
}
};
Автор:
var theThing = {
name,
version,
run: function () {
console.log(`You're using ${this.name}@${this.version}`);
}
};
Синтаксическая функция arrow изменяет способ this
работы
Ответ №2:
Функции со стрелками не привязываются к this
(смотрите здесь). Поэтому, когда вы определяете функцию, объект еще не существует … поэтому Babel не знает о контексте.
Избавление от функции arrow исправит это.
Ответ №3:
Функция стрелки this
указывает на window
из-за лексической привязки. При вызове theThing.run()
вы фактически получаете window.name
и window.version
в шаблоне. Если вы переименуете глобальные переменные, это не сработает:
var n = "MyName";
var v = "1.2.3";
var theThing = {
name: n,
version: v,
run: () => {
console.log(`You're using ${this.name}@${this.version}`);
}
};
theThing.run();
Чтобы решить эту проблему, не используйте функцию arrow:
var n = "MyName";
var v = "1.2.3";
var theThing = {
name: n,
version: v,
run() {
console.log(`You're using ${this.name}@${this.version}`);
}
};
theThing.run();
Ответ №4:
Правильный ответ состоит из 2 баллов:
- функции со стрелками и классические функции ES5 имеют совершенно разную
this
привязку.this
в классических функциях он очень динамичен (определяется во время выполнения, независимо от того, где он определен), тогда как в arrows он лексически привязан (привязан к тому месту, где он размещен, среда выполнения на это не влияет, время выполнения уже слишком поздно это менять). Из-за лексической области видимости функция arrow заимствуетthis
из ближайшей заключающей классической функции, если она присутствует. Если нет, то оно взято из глобальной области видимости… - если не в строгом режиме,
window
выполняется. Если в строгом режиме,undefined
выполняется
… что точно отвечает на приведенный выше вопрос.