0
点赞
收藏
分享

微信扫一扫

【JavaScript】this、call、apply、bind


目录

  • ​​一、 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声明的变量会输出什么?

【JavaScript】this、call、apply、bind_js


最终输出了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的指向的流程:

【JavaScript】this、call、apply、bind_bind_02

二、改变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是创建一个新的函数,需要手动调用才行。


举报

相关推荐

0 条评论