JavaScript中的this/call/apply/bind的使用及区别

一、this

1.什么是this

this 关键字在大部分语言中都是一个重要的存在,JS中自然不例外,其表达的意义丰富多样甚至有些复杂,深刻理解this是学习JS、面向对象编程非常重要的一环。

2.this 代表什么

this代表函数(方法)执行的上下文环境(上下文,类似与你要了解一篇文章,了解文章的上下文你才能清晰的了解各种关系)。

但在 JavaScript 中 this 不是固定不变的,它会随着执行环境的改变而改变。

1.在方法中,this 表示该方法所属的对象。

2.如果单独使用,this 表示全局对象。

3.在函数中,this 表示全局对象。

4.在函数中,在严格模式下,this 是未定义的(undefined)。

5.在事件中,this 表示接收事件的元素。

6.类似 call() 和 apply() 方法可以将 this 引用到任何对象。

3.绑定 this 的方法

this的动态切换,固然为 JavaScript 创造了巨大的灵活性,但也使得编程变得困难和模糊。有时,需要把this固定下来,避免出现意想不到的情况。JavaScript 提供了call、apply、bind这三个方法,来切换/固定this的指向。

4.this的指向

1.在一般函数方法中使用 this 指代全局对象

function test(){
    this.x = 1; //这里this就是window
    console.log(this.x);
  }
  test(); // 1

JS规定,函数中的this,在函数被调用时确定,它指函数当前运行的环境。

2.作为对象方法调用,this 指代上级对象

var x =3;
function test(){
  alert(this.x);
}
var o = {
 x:1,
 m:test
};
o.m(); // 1

如果函数作为对象的方法时,方法中的 this 指向该对象。

3.作为构造函数调用,this 指代new 出的对象

function test(){
    console.log(this);
  }
  var o = new test();
    test();
//可以看出o代表的不是全局对象

new关键词的作用是调用某个函数并拿到其中的返回值,只是调用过程稍特殊。在上面的代码实例中。test函数被new关键词调用时,内部依次执行了以下步骤:

(1)创建一个空对象。

(2)将这个空对象的原型,指向这个构造函数的prototype。

(3)将空对象的值赋给函数内部的this(this就是个空对象了)。

(4)执行函数体代码,为this这个对象绑定键值对。

(5)返回this,将其作为new关键词调用oop函数的返回值。

所以构造函数中的this,依旧是在构造函数被new关键词调用时确定其指向,指向的是当前被实例化的那个对象。

4.箭头函数中的this

箭头函数是ES6的新特性,最重要的特点是它会捕获其所在上下文的this作为自己的this,或者说,箭头函数本身并没有this,它会沿用外部环境的this。也就是说,箭头函数内部与其外部的this是保持一致的。

this.a=20
var test={
  a:40,
  init:()=>{
    console.log(this.a)
    function go(){
      this.a=60
      console.log(this.a)
    }
    go.prototype.a=50
    return go
  }
}

var p=test.init()
p()
new (test.init())()
//输出 20 60 60 60

5.改变指向

this的动态切换,固然为 JavaScript 创造了巨大的灵活性,但也使得编程变得困难和模糊。有时,需要把this固定下来,避免出现意想不到的情况。JavaScript 提供了call、apply、bind这三个方法,来切换/固定this的指向。

bind方法和apply、call稍有不同,bind方法返回一个新函数,以后调用了才会执行,但apply、call会立即执行。

二、Function.prototype.bind()

bind()方法主要就是将函数绑定到某个对象,bind()会创建一个函数,函数体内的this对象的值会被绑定到传入bind()中的第一个参数的值,例如:f.bind(obj),实际上可以理解为obj.f(),这时f函数体内的this自然指向的是obj;

示例:

function f(y, z){
  return this.x + y + z;
}
var m = f.bind({x : 1}, 2);
console.log(m(3));
//6

这里bind方法会把它的第一个实参绑定给f函数体内的this,所以这里的this即指向{x : 1}对象,从第二个参数起,会依次传递给原始函数,这里的第二个参数2,即是f函数的y参数,最后调用m(3)的时候,这里的3便是最后一个参数z了,所以执行结果为1 + 2 + 3 = 6分步处理参数的过程其实是一个典型的函数柯里化的过程(Curry)。

三、call/apply

1.定义

每个函数都包含两个非继承而来的方法:call()方法和apply()方法。

call和apply可以用来重新定义函数的执行环境,也就是this的指向;call和apply都是为了改变某个函数运行时的context,即上下文而存在的,换句话说,就是为了改变函数体内部this的指向。

2.语法

call()

调用一个对象的方法,用另一个对象替换当前对象,可以继承另外一个对象的属性,它的语法是:

Function.call(obj[, param1[, param2[, [,...paramN]]]]);
  • obj:这个对象将代替Function类里this对象
  • params:一串参数列表

说明:call方法可以用来代替另一个对象调用一个方法,call方法可以将一个函数的对象上下文从初始的上下文改变为obj指定的新对象,如果没有提供obj参数,那么Global对象被用于obj。

apply()

和call()方法一样,只是参数列表不同,语法:

Function.apply(obj[, argArray]);

  • obj:这个对象将代替Function类里this对象
  • argArray:这个是数组,它将作为参数传给Function

说明:如果argArray不是一个有效数组或不是arguments对象,那么将导致一个TypeError,如果没有提供argArray和obj任何一个参数,那么Global对象将用作obj。

3.异同

相同点

call()和apply()方法的相同点就是这两个方法的作用是一样的。都是在特定的作用域中调用函数,等于设置函数体内this对象的值,以扩充函数赖以运行的作用域。

一般来说,this总是指向调用某个方法的对象,但是使用call()和apply()方法时,就会改变this的指向,看个例子:

function add(a, b) {
  return a + b;
}

function sub(a, b) {
  return a - b;
}

console.log(add.call(sub, 2, 1));//3

为什么add.call(sub, 2, 1)的执行结果是3呢,因为call()方法改变了this的指向,使得sub可以调用add的方法,也就是用sub去执行add中的内容,再来看一个例子:

function People(name, age) {
  this.name = name;
  this.age = age;
}

function Student(name, age, grade) {
  People.call(this, name, age);
  this.grade = grade;
}

var student = new Student('小明', 21, '大三');
console.log(student.name + student.age + student.grade);//小明21大三

在这个例子中,我们并没有给Student的name和age赋值,但是存在这两个属性的值,这还是要归功于call()方法,它可以改变this的指向。

在这个例子里,People.call(this, name, age);中的this代表的是Student,这也就是之前说的,使得Student可以调用People中的方法,因为People中有this.name = name;等语句,这样就将name和age属性创建到了Student中。

总结一句话就是call()可以让括号里的对象来继承括号外函数的属性。

至于apply()方法作用也和call()方法一样,可以这么写:

People.apply(this, [name, age]);

或者这么写:

People.apply(this, arguments);

在这里arguments和[name, age]是等价的。

不同点

从定义中也可以看出来,call()和apply()的不同点就是接收参数的方式不同。

1.apply()方法接收两个参数,一个是函数运行的作用域(this),另一个是参数数组。
2.call()方法不一定接受两个参数,第一个参数也是函数运行的作用域(this),但是传递给函数的参数必须列举出来。

在给对象参数的情况下,如果参数的形式是数组的时候,比如之前apply()方法示例里面传递了参数arguments,这个参数是数组类型,并且在调用Person的时候参数的列表是对应一致的(也就是Person和Student的参数列表前两位是一致的)就可以采用apply()方法。

但是如果Person的参数列表是这样的(age,name),而Student的参数列表是(name,age,grade),这样就可以用call()方法来实现了,也就是直接指定参数列表对应值的位置Person.call(this,age,name)。

到此这篇关于JavaScript中的this/call/apply/bind的使用及区别的文章就介绍到这了,更多相关JavaScript this/call/apply/bind内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 详解JS中的this、apply、call、bind(经典面试题)

    这又是一个面试经典问题~/(ㄒoㄒ)/~~也是 ES5中众多坑中的一个,在 ES6 中可能会极大避免 this 产生的错误,但是为了一些老代码的维护,最好还是了解一下 this 的指向和 call.apply.bind 三者的区别. this 的指向 在 ES5 中,其实 this 的指向,始终坚持一个原理:this 永远指向最后调用它的那个对象,来,跟着我朗读三遍:this 永远指向最后调用它的那个对象,this 永远指向最后调用它的那个对象,this 永远指向最后调用它的那个对象.记住这句话

  • JS中改变this指向的方法(call和apply、bind)

    this是javascript的一个关键字,随着函数使用场合不同,this的值会发生变化.但是总有一个原则,那就是this指的是调用函数的那个对象. this一般指向的是当前被调用者,但也可以通过其它方式来改变它的指向,下面将介绍三种方式: 1.call用作继承时: function Parent(age){ this.name=['mike','jack','smith']; this.age=age; } function Child(age){ Parent.call(this,age);

  • 浅谈JavaScript中的apply/call/bind和this的使用

    fun.apply(context,[argsArray]) 立即调用fun,同时将fun函数原来的this指向传入的新context对象,实现同一个方法在不同对象上重复使用. context:传入的对象,替代fun函数原来的this: argsArray:一个数组或者类数组对象,其中的数组参数会被展开作为单独的实参传给 fun 函数,需要注意参数的顺序. fun.call(context,[arg1],[arg2],[-]) 同apply,只是参数列表不同,call的参数需要分开一个一个传入.

  • JavaScript中的this/call/apply/bind的使用及区别

    一.this 1.什么是this this 关键字在大部分语言中都是一个重要的存在,JS中自然不例外,其表达的意义丰富多样甚至有些复杂,深刻理解this是学习JS.面向对象编程非常重要的一环. 2.this 代表什么 this代表函数(方法)执行的上下文环境(上下文,类似与你要了解一篇文章,了解文章的上下文你才能清晰的了解各种关系). 但在 JavaScript 中 this 不是固定不变的,它会随着执行环境的改变而改变. 1.在方法中,this 表示该方法所属的对象. 2.如果单独使用,thi

  • 深入理解JavaScript中的call、apply、bind方法的区别

    在JavaScript 中,this的指向是动态变化的,很可能在写程序的过程中,无意中破坏掉this的指向,所以我们需要一种可以把this的含义固定的技术,于是就有了call,apply 和bind这三个方法,来改变函数体内部 this 的指向,因为函数存在「定义时上下文」和「运行时上下文」以及「上下文是可以改变的」这样的概念 apply.call apply:应用某一对象的一个方法,用另一个对象替换当前对象 call:调用一个对象的一个方法,以另一个对象替换当前对象 function pers

  • 浅谈javascript中的call、apply、bind

    在JavaScript中,call.apply和bind 是Function对象自带的三个方法,这三个方法的主要作用是改变函数中的this指向,从而可以达到`接花移木`的效果.本文将对这三个方法进行详细的讲解,并列出几个经典应用场景. call(thisArgs [,args...]) 该方法可以传递一个thisArgs参数和一个参数列表,thisArgs指定了函数在运行期的调用者,也就是函数中的this对象,而参数列表会被传入调用函数中.thisArgs的取值有以下4种情况: (1) 不传,或

  • JavaScript中innerHTML,innerText,outerHTML的用法及区别

    不废话了,请看下文示例介绍. 用法: <div id="test"> <span style="color:red">test1</span> test2 </div> 在JS中可以使用: test.innerHTML: 也就是从对象的起始位置到终止位置的全部内容,包括Html标签. 上例中的test.innerHTML的值也就是"<span style="color:red">

  • JavaScript中变量声明有var和没var的区别示例介绍

    本文来论述JavaScript中变量声明有var和没var的区别,关于Js中的变量声明的作用域是以函数为单位,所以我们经常见到避免全局变量污染的方法是 (function(){ // ... })(); 在函数内部,有var和没var声明的变量是不一样的.有var声明的是局部变量,没var的,声明的全局变量,所以可以借此向外暴露接口东东. 在全局作用域内声明变量时,有var 和没var看起来都一样,我们知道,声明的全局变量,就是window的属性,究竟是否一样,我们通过ECMAScrpit5提供

  • JavaScript中三个等号和两个等号的区别(== 和 ===)浅析

    == equality 等同,=== identity 恒等. ==, 两边值类型不同的时候,要先进行类型转换,再比较. ==,不做类型转换,类型不同的一定不等. 下面分别说明: 先说 ===,这个比较简单.下面的规则用来判断两个值是否===相等: 1.如果类型不同,就[不相等] 2.如果两个都是数值,并且是同一个值,那么[相等]:(!例外)的是,如果其中至少一个是NaN,那么[不相等].(判断一个值是否是NaN,只能用isNaN()来判断) 3.如果两个都是字符串,每个位置的字符都一样,那么[

  • 浅谈JavaScript中等号、双等号、 三等号的区别

    一个等号是赋值操作,==先转换类型再比较,===先判断类型,如果不是同一类型直接为false. === 判断规则 如果类型不同,就[不相等]  如果两个都是数值,并且是同一个值,那么[相等]:(!例外)的是,如果其中至少一个是NaN,那么[不相等].(判断一个值是否是NaN,只能用isNaN()来判断)  如果两个都是字符串,每个位置的字符都一样,那么[相等]:否则[不相等].  如果两个值都是true,或者都是false,那么[相等].  如果两个值都引用同一个对象或函数,那么[相等]:否则[

  • JavaScript中的this,call,apply使用及区别详解

    学习起因: 在之前的JavaScript学习中,this,call,apply总是让我感到迷惑,但是他们的运用又非常的广泛.遂专门花了一天,来弄懂JavaScript的this,call,apply. 中途参考的书籍也很多,以<JavaScript设计模式与开发实践>为主,<JavaScript高级程序设计>.<你不知道的JavaScript>为辅.这三本书对我理解this,call,apply都起了很大的帮助. this 首先,我们先讲述this. 在<Java

  • javascript中使用new与不使用实例化对象的区别

    我们先来看个实例 function Me(name,age,job){ this.name = name; this.age = age; this.job = job; } 请问这以下两种实例化对象方式有什么区别呢? var mefun1 = new Me('fei','20','it'); var mefun2 = Me('fei','20','it'); 简单的说 第一种是构造函数式,即通过new运算符调用构造函数Function来创建函数 第二种不是实例化,只是调用函数把返回值赋给变量.

  • 在Javascript中 声明时用"var"与不用"var"的区别

    Javascript声明变量的时候,虽然用var关键字声明和不用关键字声明,很多时候运行并没有问题,但是这两种方式还是有区别的.可以正常运行的代码并不代表是合适的代码. var num = 1: 是在当前域中声明变量. 如果在方法中声明,则为局部变量(local variable):如果是在全局域中声明,则为全局变量. 而 num = 1: 事实上是对属性赋值操作.首先,它会尝试在当前作用域链(如在方法中声明,则当前作用域链代表全局作用域和方法局部作用域etc...)中解析 num: 如果在任何

随机推荐