javacript 变量、函数声明、函数表达式之声明提升详解
变量声明提升
变量声明提升至项部。
if (false) {
var a = 10;
}
console.log(a); // undefined
// 相当于
var a;
if (false) {
a = 10;
}
console.log(a); // undefined
结论:由于变量声明提升,故输出 undefined,而非报错信息。 Uncaught ReferenceError: a is not defined。
函数声明提升
函数声明提升至顶部。
foo();
function foo() {
console.log(1)
}
// 相当于
function foo() {
console.log(1)
}
foo();
结论:由于函数声明提升,故输出 1,而非报错信息。 Uncaught TypeError: foo is not defined。
函数表达式提升
只是变量声明提升,函数表达式函数体并没有提升。
console.log(1, foo)
foo();
var foo = function() {
console.log(1)
}
// 相当于
var foo;
console.log(foo)
foo();
foo = function() {
console.log(1)
}
结论:由于只是变量声明提升,故输出 undefined 和 Uncaught TypeError: foo is not a function
函数提升优先级高
函数声明和变量声明都会被提升。但是函数会首先被提升,其次才是变量。
foo();
function foo() {
console.log(1);
}
var foo = function() {
console.log(2);
}
结论:输出1非2,或者Error。
提升后 this 作用域
let o = {
m: function() {
let self = this;
console.log(this === o); // true this 是对象o
foo();
function foo() {
console.log(this === o); // false this 是全局对象或 undefined
console.log(self === o); // true self 是外部的 this 值
}
}
}
o.m();
在嵌套函数 foo() 内部,this 关键字不等于对象 o,这被广泛认为是 javascript 语言的一个缺陷。那如何解决了,如上代码所示可以用 self 赋值,那有没有其它办法了,答案是当然有的。
es6 的箭头函数 和 bind() 都可以解决。
let o = {
m: function() {
let self = this;
console.log(this === o); // true this 是对象o
// function foo() {
// console.log(this === o); // false this 是全局对象或 undefined
// console.log(self === o); // true self 是外部的 this 值
// }
const foo = () => {
console.log(this, this === o); // true this 是对象o
console.log(self, self === o); // true this 是对象o
}
// const foo = (function() {
// console.log(this === o); // true this 是对象o
// }).bind(this);
foo();
}
}
o.m();
注意:这里使用的是函数表达式声明,函数体并没有提升,所以函数调用需要移下来,不然就会报 foo is not function。
构造函数调用
new o.m() 会是什么样的效果了?
let o = {
m: function() {
let self = this;
console.log(this === o); // true this 是对象o
// function foo() {
// console.log(this === o); // false this 是全局对象或 undefined
// console.log(self === o); // true self 是外部的 this 值
// }
const foo = () => {
console.log(this, this === o);
console.log(self, self === o)
}
// const foo = (function() {
// console.log(this === o)
// }).bind(this);
foo();
}
}
new o.m();
构造函数调用会创建一个新的空对象,这个对象继承构造函数的 prototype 属性指定的对象。构造函数就是为初始化对象设计的,这个新创建的对象会被用作函数的调用上下文。