JavaScript this、call 和 apply 初识
一、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(){}'