this
this 是 JavaScript 执行上下文的一个属性,它的指向在代码运行时决定。
指向
- 函数调用,指向
window
对象 - 方法调用,指向调用该方法的对象
- 强绑定,
call
、apply
、bind
new
构造调用,指向新创建的对象
new
new
- 创建新对象
- 新对象
__proto__
指向构造函数的 prototype - 执行构造函数,this 绑定到新对象
- 构造函数返回值 == 对象 ? 返回构造函数 return 值 : 返回新对象
原理(手写)
function myNew(fn, ...args) {
let obj = Object.create(fn.prototype);
let res = fn.call(obj, ...args);
if (res && (typeof res == "object" || typeof res == "function")) {
return res;
}
return obj;
}
关键是理解Object.create
的作用,它又可以通过new
实现。Polyfill🕵️♂️
if (typeof Object.create !== "function") {
Object.create = function (proto) {
if (typeof proto !== "object" && typeof proto !== "function") {
throw new TypeError("Object prototype may only be an Object: " + proto);
}
function F() {}
F.prototype = proto;
return new F();
};
}
箭头函数
箭头函数没有自己的this
,箭头函数的 this 将绑定父级作用域上下文。
使用 setTimeout 来模拟网络请求,请求到数据后如何可以存放到 data 中呢?
var obj = {
data: [],
getData: function () {
var _this = this;
setTimeout(function () {
// 模拟获取到的数据
var res = ["abc", "cba", "nba"];
_this.data.push.apply(_this.data, res);
}, 1000);
},
};
obj.getData();
let obj = {
data: [],
getData() {
setTimeout(() => {
// 模拟获取到的数据
const res = ["abc", "cba", "nba"];
this.data.push(...res);
}, 1000);
},
};
obj.getData();
箭头函数的出现取代了 ES5 之前使用的作用域机制。
测试
函数调用
function foo(func) {
func();
}
var obj = {
name: "13pro",
bar: function () {
console.log(this);
},
};
foo(obj.bar);
answer
window 对象
方法调用
var name = "window";
var person = {
name: "person",
sayName: function () {
console.log(this.name);
},
};
function sayName() {
person.sayName(); // (person.sayName)();
(b = person.sayName)(); // var b = person.sayName; b();
}
answer
function sayName() {
// 关联
person.sayName(); // person
// 独立函数调用,没有和任何对象关联
(b = person.sayName)(); // window
}
构造调用
var name = "window";
function Person(name) {
this.name = name;
this.foo1 = function () {
console.log(this.name);
};
this.foo2 = () => console.log(this.name);
this.foo3 = function () {
return function () {
console.log(this.name);
};
};
this.foo4 = function () {
return () => {
console.log(this.name);
};
};
}
var person1 = new Person("person1");
var person2 = new Person("person2");
person1.foo1();
person1.foo1.call(person2);
person1.foo2();
person1.foo2.call(person2);
person1.foo3()();
person1.foo3.call(person2)();
person1.foo3().call(person2);
person1.foo4()();
person1.foo4.call(person2)();
person1.foo4().call(person2);
var name = "window";
function Person(name) {
this.name = name;
this.obj = {
name: "obj",
foo1: function () {
return function () {
console.log(this.name);
};
},
foo2: function () {
return () => {
console.log(this.name);
};
},
};
}
var person1 = new Person("person1");
var person2 = new Person("person2");
person1.obj.foo1()();
person1.obj.foo1.call(person2)();
person1.obj.foo1().call(person2);
person1.obj.foo2()();
person1.obj.foo2.call(person2)();
person1.obj.foo2().call(person2);
小结
- this 值在运行时确定,与代码定义位置无关
- 牢记 this 绑定的 4 种规则
- 箭头函数 this 值绑定父级作用域,无法被改变