目录
- 一、 this绑定模式
- 1. 默认绑定
- 2. 隐式绑定
- 3. 显式绑定
- 4. new绑定
- 二、改变this指向
- 1. 使用箭头函数
- 2. 在函数内使用that = this
- 3. 使用 call、apply、bind方法
- 三、call、apply、bind
- 1. call
- 2. apply
- 3. bind
- 4. 三者区别
一、 this绑定模式
this
有四种绑定模式,分别是默认绑定、隐式绑定、显式绑定、new绑定
优先级关系:new绑定 > 显式绑定 > 隐式绑定 > 默认绑定
1. 默认绑定
默认绑定模式下,this
默认指向顶层对象window(浏览器环境指 window、Node.js 环境指 Global)
var str = 'hello world'
function log() {
console.log(this.str)
}
log() // hello world
但是,如果在严格模式下,就会输出undefined。
我们再来看一下,如果使用let或者const声明的变量会输出什么?
最终输出了undefined,这是因为ES6中的let和const声明的变量和常量不再是顶层对象了。所谓的顶层对象,在浏览器环境指的是 window,在Node.js 环境指的是Global。顶层对象的属性和全局变量的属性是相等价的。使用var、function声明的是顶层对象的属性,用let、const、class声明的是全局变量,不再属于顶层对象的属性。
2. 隐式绑定
隐式绑定是指:在一个对象的内部通过属性间接引用函数,从而把 this 隐式绑定到对象内部属性所指向的函数。在隐式绑定模式下,this
指向调用方法的对象
var name = "css";
var person = {
name: "js",
getName: function(){
return this.name;
}
}
console.log(person.getName()); // js
如果我们将代码进行如下修改,结果会怎样呢?
var name = "css";
var person = {
name: "js",
getName: function(){
return this.name;
}
}
var person1= person.getName
console.log(person1()); // css
输出结果就变成了顶层对象的name属性值。这是因为person1()知识一个不带任何修饰符的函数调用,因此使用的是默认绑定。
3. 显式绑定
显式绑定是指需要引用一个对象时进行强制绑定调用,显式绑定主要是使用apply、call、bind
方法来绑定this
值:
var person = {
name: "hello",
};
function say(job){
console.log(this.name+":"+job);
}
say.call(person,"js"); // hello:js
4. new绑定
new绑定主要是在构造函数中,this指向构造的新对象:
function Person(name,age){
this.name = name;
this.age = age;
this.say = function(){
console.log(this.name + ":" + this.age);
}
}
var person = new Person("js",18);
console.log(person.name); // js
console.log(person.age); // 18
person.say(); // js:18
在上面的代码中,this就指向了构造函数Person的新对象person,所以使用this可以获取到person对象的属性和方法。
判断this的指向的流程:
二、改变this指向
1. 使用箭头函数
- 箭头函数不绑定this,它会捕获其所在上下文的this值,作为自己的this值。
- 箭头函数中没有 this 绑定,必须通过查找作用域链来决定其值,如果箭头函数被非箭头函数包含,则 this 绑定的是最近一层非箭头函数的 this,否则,this 为 undefined
- 箭头函数常用于回调函数中,例如定时器
var name = "windowsName";
var a = {
name : "Cherry",
func1: function () {
console.log(this.name)
},
func2: function () {
setTimeout( () => {
this.func1()
},100);
}
};
a.func2() // Cherry
如果不使用箭头函数,就会报错,因为定时器setTimeout的对象是window,使用箭头函数之后,就改变了func2的this指向,指向上层函数所在的作用域,也就是a对象。
2. 在函数内使用that = this
- 先将调用这个函数的
this
保存在that
中,然后在函数中使用这个that
,这样this
就不会改变了
var name = "windowsName";
var a = {
name : "Cherry",
func1: function () {
console.log(this.name)
},
func2: function () {
var that = this; // 相当于将this绑定在调用func2的对象上了,不会被改变
setTimeout( function() {
that.func1()
},1000);
}
};
a.func2() // Cherry
3. 使用 call、apply、bind方法
(1)call()
、apply()
: this绑定在指定对象上,绑定的函数会直接执行
var person = {
name: "hello",
};
function say(job){
console.log(this.name+":"+job);
}
say.call(person,"js"); // hello:js
(2)bind()
:this绑定在bind的第一个参数,会创建一个新的函数,需要手动调用
var person = {
name: "hello",
age: 18
};
function say(){
console.log(this.name+":"+this.age);
}
var f = say.bind(person);
console.log(f()); // hello:18
三、call、apply、bind
1. call
(1)语法:function.call(thisArg, arg1, arg2, ...)
(2)参数:thisArg
是指在函数运行时制定的this值,arg1,arg2...
是参数列表
(3)返回值:返回值是调用的方法的返回值,如果没有,就返回undefined
(4)作用:使用一个指定的 this
值和若干个指定的参数值的前提下调用某个函数或方法。
基本实现:
Function.prototype.myCall = function (context) { //判断调用对象
if (typeof this !== "function") {
console.error("type error");
}
//获取参数
let args = [...arguments].slice(1)
let result = null;
//判断context是否传入,如果未传入则设置为window
context = context || window;
//将调用函数设为对象的方法
context.fn = this;
//调用函数
result = context.fn(...args);
//将属性删除
delete context.fn;
return result;
}
2. apply
(1)语法:func.apply(thisArg, [argsArray])
(2)参数:thisArg
是指在函数运行时制定的this值,argsArray
是参数数组
(3)返回值:返回值是调用的方法的返回值,如果没有,就返回undefined
(4)作用:使用一个指定的 this
值和若干个指定的参数值的前提下调用某个函数或方法。
基本实现:
Function.prototype.myApply = function (context) {
//判断调用对象是否为函数
if (typeof this !== "function") {
throw new TypeError("Error");
let result = null;
//判断context是否存在,如果未传入则为window
context = context || window;
//将函数设为对象的方法
context.fn = this;
//调用方法
if (arguments[1]) {
result = context.fn(...arguments[1]):
} else (
result = context.fn():
}
//将属性删除
delete context.fn;
return result;
}
3. bind
(1)bind()
方法创建一个新的函数,在 bind()
被调用时,这个新函数的 this 被指定为 bind()
的第一个参数,而其余参数将作为新函数的参数,供调用时使用。
(2)语法:function.bind(thisArg[, arg1[, arg2[, ...]]])
(3)参数:thisArg
是调用绑定函数时作为 this
参数传递给目标函数的值
(4)返回值:返回一个原函数的拷贝,并拥有指定的 this
值和初始参数
基本实现:
Function.prototype.myBind = function (context) (
//判断调用对象是否为函数
if (typeof this !== "function") {
throw new TypeError("Error");
}
//获取参数
let args = [...arguments].slice(1),
let fn = this;
return function Fn() {
//根据调用方式,传入不同绑定值
return fn.apply(this instanceof Fn ? this : context, args.concat(...arguments));
}
}
4. 三者区别
(1)apply 和 call 的区别?
这两个方法的用法基本一致,只是传入的参数形式不一样, call 方法接受的是若干个参数列表,而 apply 接收的是一个包含多个参数的数组。
(2)bind 和 apply、call 区别?
使用call和apply的函数会直接执行,bind是创建一个新的函数,需要手动调用才行。