一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第14天,点击查看活动详情
this是指向当前函数的运行环境的上下文, 可以当做是一个指针, 可以理解为一个动态的对象, 普通函数中的this是在调用时确认指向. this的出现使我们在使用函数时候可以使用不同的上下文, 通过不同的this调用同一个函数, 可以产出不同的结果。下面我们来说说this的绑定规则
this的绑定规则
1. 默认绑定
指函数独立调用的时候,不带任何修饰的函数引用。
- 非严格模式下 指向全局对象(浏览器下指向 Window,Node.js 环境是 Global )
- 严格模式下,严格模式不允许this指向全局对象。会显示undefined , 比如如下代码, 如果我们将代码在浏览器环境下执行, 严格模式和非严格模式的结果是不同的:
- 非严格模式会输出 hello
- 严格模式会出一个Uncaught TypeError: Cannot read properties of undefined的错误
var a = 'aaa'
var obj = {
a: 'bbb',
foo: function() {
// 'use strict';
console.log(this.a)
}
}
var bar = obj.foo
bar() // aaa
普通函数做为参数传递的情况, 比如setTimeout, setInterval, 非严格模式下的this指向全局对象
var name = 'aaa';
var person = {
name: 'hahahahahah',
sayHi: sayHi
}
function sayHi(){
console.log(this); // { name: hahahahhah, sayHi: Fn }
setTimeout(function(){
console.log('Hello,', this.name); // Hello, aaa
})
}
person.sayHi();
2. 隐式绑定
与默认绑定相反, 谁调用则指向谁,比如说某个对象调用的函数则指向某个对象。 比如下面这段代码, foo 方法是作为对象的属性调用的,this 指向 obj 对象。 所以但下面代码obj调用foo函数的时候,。this指向的是obj
var a = 'hello'
var obj = {
a: 'aaa',
foo: function() {
console.log(this.a)
}
}
obj.foo(); // aaa
链式调用的情况,链式调用遵循就近原则,从下面我们可以清晰的看到,是指向离调用函数最近的对象friend,friend是preson2所以打印出的this.name是aaa
function sayHi(){
console.log('Hello,', this.name);
}
var person2 = {
name: 'aaa',
sayHi: sayHi
}
var person1 = {
name: 'hahhahaahh',
friend: person2
}
person1.friend.sayHi(); // Hello, aaa
3. 显式绑定
显示绑定指的是通过函数call apply bind 可以修改函数this的指向
call 和 apply
call和apply都是改变函数的this指向并且执行
- call和apply的第一个参数会绑定到函数体的this上,如果不传参数,例如fun.call(),非严格模式,this默认还是绑定到全局对象
- call和apply只有在接受参数上有所区别,call函数接收的是一个参数列表,apply函数接收的是一个参数数组。
func.call(this, arg1, arg2, ...)
func.apply(this, [arg1, arg2, ...])
var person = {
"name": "aaa"
};
function changeWork(company, work) {
this.company = company;
this.work = work;
};
changeWork.call(person, 'company1', 'bbb');
console.log(person.work); // 'bbb'
changeWork.apply(person, ['company2', 'ccc']);
console.log(person.work); // 'ccc'
调用call和apply时, 传入的是基本类型数字或者字符串, 绑定this的时候会把他们转换成对象
function getThisType () {
console.log('this指向内容',this, typeof this);
}
getThisType.call(1); // this指向内容 Number {1} object
getThisType.apply('aaa'); // this指向内容 String {'aaa'} object
bind
bind 方法会创建一个新函数。当这个新函数被调用时,bind() 的第一个参数将作为它运行时的 this,之后的一序列参数将会在传递的实参前传入作为它的参数.
func.bind(thisArg[, arg1[, arg2[, ...]]])
看下面这段代码
var publicAccounts = {
name: 'aaa',
author: 'bbb',
subscribe: function(subscriber) {
console.log(`${subscriber} ${this.name}`)
}
}
publicAccounts.subscribe('bbb')
var subscribe1 = publicAccounts.subscribe.bind({ name: '测试名称A', author: '测试作者B' }, '测试订阅者C')
subscribe1() // 测试订阅者C 测试名称A
4. new绑定
new绑定的前提我们先来了解一下new做了 哪几个操作
- 创建一个空对象,可以是object.creat()
- 将空对象的 proto 指向原对象的 prototype
- 执行构造函数中的代码
- 返回这个新对象
构造函数中的this指向了新生成的实例studyDay.
function study(name){
this.name = name;
}
var studyDay = new study('aaa');
console.log(studyDay); // {name: 'aaa'}
console.log(studyDay.name); // aaa
5. this绑定的优先级
new绑定 > 显式绑定 > 隐式绑定 > 默认绑定
显示绑定优先级比隐式绑定更高。
function foo() {
console.log(this.a)
}
var obj1 = {
a: 2,
foo: foo
}
var obj2 = {
a: 3,
foo: foo
}
obj1.foo(); // 2
obj2.foo(); // 3
obj1.foo.call(obj2); // 3
obj2.foo.call(obj1); // 2
function foo(something) {
this.a = something
}
var obj1 = {
foo: foo
}
var obj2 = {}
obj1.foo(2);
console.log(obj1.a); // 2
obj1.foo.call(obj2, 3);
console.log(obj2.a); // 3
var bar = new obj1.foo(4);
console.log(obj1.a); // 2
console.log(bar.a); //4
function foo(something) {
this.a = something
}
var obj1 = {
}
var bar = foo.bind(obj1);
bar(2);
console.log(obj1.a); //2
var baz = new bar(3);
console.log(obj1.a); // 2
console.log(baz.a); // 3