JavaScript this、call 和 apply 初识

作者: tww844475003 分类: 前端开发 发布时间: 2021-06-03 19:07

一、this

this 指向

  • 作为对象的方法调用

当函数做为对象的方法调用时,this 指向该对象

var obj = {
  name: 'javaScript',
  getName: function() {
    console.log(this === obj); // true
    console.log(this.name); // 'javascript'
  }
}

obj.getName();
  • 作为普通调用

当函数作为普通函数方式调用时,this 总是指向全局对象

window.name = 'javaScript';

var getName = function() {
  return this.name
}

console.log(getName()) // 'javaScript'
  • 作为构造器调用
var Person = function() {
  this.name = 'javaScript'
}

var obj = new Person();
console.log(obj.name) // javaScript

如果构造器显示地返回了一个 Object 类型的对象,那么运算结果最终会返回这个对象,而不是我们之前期待的 this

var Person = function() {
  this.name = 'javaScript'

  // 显示返回一个对象
  return {
    name: 'updated javaScript'
  }
}

var obj = new Person();
console.log(obj.name) // updated javaScript

当用 new 运算符调用函数时,该函数总会返回一个对象,通常构造器里的 this 就指向返回的这个对象

  • Function.prototype.call 或 Function.prototype.apply 调用

跟普通的函数调用相比,用 Function.prototype.call 或 Function.prototype.apply 可以动态地改变传入函数的 this

var person = {
  name: 'javaScript',
  getName: function() {
    return this.name
  }
}

var obj2 = {
  name: 'updated javaScript'
}

console.log( person.getName() ) // javaScript
console.log( person.getName.call(obj2) ) // updated javaScript
  • 丢失的 this
var person = {
  name: 'javaScript',
  getName: function() {
    return this.name
  }
}

console.log( person.getName() ) // javaScript
var getName2 = person.getName;
console.log( getName2() ) // undefined

二、call 和 apply

  • call 和 apply 的区别

apply 接爱两个参数,第一个参数指定了函数体内 this 对象的指向,第二个参数为一个带下标的集合(数组、类数组),apply 方法把这个集合中的元素作为参数传递给被调用的函数

var func = function(a, b, c) {
  console.log( [a, b, c] ); // [1, 2, 3]
}
func.apply(null, [1, 2, 3])

call 传入的参数数量不固定,跟 apply 相同的是,第一个参数也是代表函数体内的 this 指向,从第二个参数开始往后,每个参数被依次传入函数

var func = function(a, b, c) {
  console.log( [a, b, c] ); // [1, 2, 3]
}
func.call(null, 1, 2, 3)
  • call 和 apply 的用途

call 和 apply 最常见的用途是改变函数内部的 this 指向

var obj1 = {
  name: '张三'
}

var obj2 = {
  name: '李四'
}

window.name = 'javaScript';

var getName = function() {
  console.log( this.name );
}

getName(); // window
getName.call( obj1 ); // 张三
getName.call( obj2 ); // 李四

Function.prototype.bind

Function.prototype.bind = function(context) {
  var self = this; // 保存原函数

  return function() {
    // 执行新的函数的时候,把之前传入的 context 当作新函数体内的 this
    return self.apply(context, arguments);
  }
}

var obj = {
  name: 'javaScript'
}

var func = function() {
  console.log( this.name ); // javaScript
}.bind(obj);

func();

借用其他对象的方法

借用构造函数,通过这种技术,可以实现类似继承的效果

var Parent = function(name) {
  this.name = name;
}

var Child = function() {
  Parent.apply( this, arguments );
}

Child.prototype.getName = function() {
  return this.name;
}

var child = new Child('javaScript');
console.log(child.getName()); // javaScript

函数的参数列表 arguments 是一个类数组对象,虽然它也有下标,但它并非真正的数组,所以也不能像数组一样,进行排序操作或者往集合里添加一个新元素,这种情况,通常可以借用 Array.prototype

(function() {
  Array.prototype.push.call(arguments, 3);
  console.log(arguments)
})(1, 2)

借用 Array.protype.push 方法的对象需要满足两个条件

  • 对象本身要可以存取属性;
  • 对象的 length 属性可读写
var a = 1;
Array.prototype.push.call(a, 'first');
console.log(a.length); // undefined
console.log(a[0]); // undefined

var func = function(){};
Array.prototype.push.call(func, 'first');
console.log(func.length); // Uncaught TypeError: Cannot assign to read only property 'length' of function 'function(){}'

前端开发那点事
微信公众号搜索“前端开发那点事”

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注