this的指向和分析
JavaScrip高级教程 · 143 字
this的作用
this常出现在类中,可以指向自身
this的指向
全局环境指向
- 浏览器环境:window(globalOject)
- Node环境:空对象{}。Node中调用JS时当作module → 加载→编译→放在函数中→执行函数.call()
不同执行方式的不同指向
动态绑定 — 函数执行时才能确定this指向谁
-
默认绑定:没有明确的调用主体,独立的函数调用。指向window
function foo1 () { console.log(this); } function foo2 () { console.log(this); foo1(); } function foo3 () { console.log(this); foo2(); } // 输出结果全为windowvar obj = { name: "why", foo: function() { console.log(this) } } var bar = obj.foo; bar() // 输出结果为window -
隐式绑定:通过某个对象发起函数调用。object对象会被js引擎绑定到fn函数中的this里面
function foo() { console.log(this); } var obj = { name: "why", foo: foo } obj.foo() -
显式绑定:call、apply、bind
-
call与apply的区别:call传入参数列表,apply传入参数数组
-
bind:返回一个信函数,永久改变this指向,传入参数数组
function foo() { console.log(this); } var newFoo = foo.bind("aaa")
-
-
new绑定
- 创建一个全新对象
- 这个新对象被执行prototyp链接
- 这个新对象会绑定到函数调用的this上
- 如果函数没有返回其他对象,表达式返回这个新对象
function Person(name, age) { this.name = name; this.age = age; } var p = new Person("why", 18) console.log(p1.name, p1.age); // why 18
系统API中的this指向
-
setTimeout
setTimeout(function() { console.log(this); // window }, 2000) -
监听点击
const boxDiv = document.querySelector(".box"); boxDiv.onClick = function() { console.log(this); // 指向绑定的dom元素 } -
数组.forEach/map/filter…
var names = []; names.forEach(function() { console.log(this); // 默认时为window,第二个参数thisArg能指定this指向 }, thisArg)
多个绑定规则的优先级
-
默认规则的优先级最低
-
显式绑定优先级高于隐式绑定
function foo() { console.log(this); } var obj = { name: "obj", foo: foo.bind("aaa") } obj.foo(); // {String: "aaa"} -
new绑定优先级高于隐式绑定
var obj = { name: "obj", foo: function() { console.log(this); } } var f = new obj.foo(); // foo {} -
new绑定优先级高于bind

function foo() { console.log(this); } var bar = foo.bind("aaa"); var obj = new bar(); // foo {}
忽略显式绑定
apply/call/bind:当传入null/undefined时,自动将this绑定为window
间接函数引用
var obj1 = {
name: "obj1",
foo: function() {
console.log(this);
}
}
var obj2 = {
name: "obj",
bar: null
};
(obj2.bar = obj1.foo)(); // 独立函数调用window
/* Uncaught TypeError: Cannot set properties of undefined (setting 'bar')
at <anonymous>:11:11*/
(obj2.bar = obj1.foo) 与上面的obj2的赋值看作是一个整体,这样执行obj2.bar时会undefined。解决方法是在obj2的赋值语句后面加一个; ,这样在进行语法分析时能清晰的知道二者为独立的语句。
箭头函数的使用
- 箭头函数不会绑定this、arguments属性;
- 不能作为构造函数来使用
常见的简写方法
- 如果参数只有一个,()可以省略
item => {} - 函数执行体只有一行代码,{}可以省略,并且默认将该行代码的执行结果作为返回值
item => console.log(item) - 只有一行代码,并且返回一个对象
() => ({ name: "why", age: 18 })
箭头函数中的this
不适用于this的四种规则,而是根据外层作用域来决定this,与定义的位置有关
var name = "why";
var foo = () => {
console.log(this);
}
foo(); // window
var obj = {foo: foo};
obj.foo(); // window
foo.call("aaa"); // window
应用场景:
var obj = {
data: [],
getData: function() {
// 发送网络请求,将结果放在上面data属性中
// 在箭头函数之前的解决方案
// var _this = this;
// setTimeout(function() {
// var result = ["abc"];
// _this.data = result;
// }, 2000)
// 箭头函数
setTimeout(() => {
var result = ["abc"];
this.data = result;
}, 2000)
}
}
面试题

var name = 'window'
var person1 = {
name: 'person1',
foo1: function () {
console.log(this.name)
},
foo2: () => console.log(this.name),
foo3: function () {
return function () {
console.log(this.name)
}
},
foo4: function () {
return () => {
console.log(this.name)
}
}
}
var person2 = { name: 'person2' }
person1.foo1(); // person1
person1.foo1.call(person2); // person2
person1.foo2(); // windows 定义对象时不产生作用域,因此上层作用域为obj的上层作用域
person1.foo2.call(person2); // window
person1.foo3()(); // window
person1.foo3.call(person2)(); // window
person1.foo3().call(person2); // person2
person1.foo4()(); // person1 返回箭头函数,上层为foo4: function... 它的this为隐式调用确定的person1
person1.foo4.call(person2)(); // person2 同上
person1.foo4().call(person2); // person1 call不改变箭头函数的this指向
,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 隐式绑定
person1.foo1.call(person2) // person2 显式绑定给
person1.foo2() // person1 箭头函数,函数是由作用域的,上层作用域为构造函数new Person('person1')
person1.foo2.call(person2) // person1 同上
person1.foo3()() // window 独立调用
person1.foo3.call(person2)() // window 同上
person1.foo3().call(person2) // person2 显式绑定
person1.foo4()() // person1 箭头函数,上层作用域为foo4,此处绑定的为person1
person1.foo4.call(person2)() // person2 上层作用域为foo4,此处显式绑定的person2
person1.foo4().call(person2) // person1
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()() // window 独立调用
person1.obj.foo1.call(person2)() // window 同上
person1.obj.foo1().call(person2) // person2 显式绑定
person1.obj.foo2()() // obj 箭头函数,foo2是被obj调起的,隐式绑定foo2的this为obj,因此上层作用域为obj
person1.obj.foo2.call(person2)() // person2 类似上面,但foo2被显式绑定为person2
person1.obj.foo2().call(person2) // obj 同第一个
自主实现call、apply、bind
call
Function.prototype.myCall = function(thisAgs, ...args){
// 1. 获取被执行函数,利用隐式绑定特性
var fn = this;
// 2. 将thisArg转成对象类型
thisAgs = (thisAgs !== null && thisAgs !== undefined) ? Object(thisAgs) : window;
// 3. 调用被执行函数
thisAgs.fn = fn;
var result = thisAgs.fn(...args);
delete thisAgs.fn;
// 4. 返回执行结果
return result;
}
apply
Function.prototype.myApply = function(thisAgs, argArray){
// 1. 获取被执行函数,利用隐式绑定特性
var fn = this;
// 2. 将thisArg转成对象类型
thisAgs = (thisAgs !== null && thisAgs !== undefined) ? Object(thisAgs) : window;
// 3. 调用被执行函数
thisAgs.fn = fn;
argArray = argArray || [];
// argArray = argArray ? argArray : []
var result = thisAgs.fn(...argArray);
delete thisAgs.fn;
// 4. 返回执行结果
return result;
}
bind
Function.prototype.myBind = function (thisAgs, ...argArray) {
// 1. 获取被执行函数,利用隐式绑定特性
var fn = this;
// 2. 将thisArg转成对象类型
thisAgs = (thisAgs !== null && thisAgs !== undefined) ? Object(thisAgs) : window;
function proxyFn(...args) {
// 3. 函数放到thisArgs上进行调用
thisAgs.fn = fn;
// 对两个传入的参数进行合并
var finalArgs = [...argArray, ...args]
var result = thisAgs.fn(...finalArgs);
delete thisAgs.fn;
// 4. 返回执行结果
return result
}
return proxyFn;
}