JS中bind,call,apply以及new的用法与实现
春招冲刺01.16日 - 01
1. 什么是bind,call,apply
bind,call,apply是JavaScript中Function对象自带的三个方法,用于改变函数体内部的 this
指向,也就是函数调用时的上下文(context)。
bind,call,apply三者都可以利用后续参数传参。其中bind不会立即调用,而是返回对应的绑定函数,其内的this指向为创建它时传入bind的 第一个参数,而传入bind的第二个及以后的参数作为 原函数的参数 来调用原函数。
bind(thisArg[, arg1[, arg2[, ...]]])
apply、call则是立即调用,改变的this指向他们的第一个参数,apply的第二个参数是一个参数数组,call的第二个及其以后的参数都是数组里面的元素。
call ( thisArg, arg1, arg2, ... )
apply ( thisArg ,[argArray] )
2. call的应用与实现
在call的语法call ( thisArg, arg1, arg2, ... )
中,thisArg的传递情况有:
- null,undefined或不传 此时指向window
- 传递另一个函数B的函数名,函数中的this指向B的引用
- 传递字符串、数值或布尔类型等基础类型,函数中的this指向其对应的包装对象,如 String、Number、Boolean
- 传递一个对象,函数中的this指向这个对象
常用方法:
function pChain (){
this.name="pChainRoot";
this.nameprint=function(){
console.log('打印的名字是',this.name);
}
}
var newname={name:"PChainChild"};
var PChain=new pChain();
PChain.nameprint.call(newname); //PChainChild
PChain.nameprint(); //pChainRoot
模拟实现:
Function.prototype.myCall = function (thisArg,...args){
//thisArg为调用mycall方法的函数的this指向
var thisArg = thisArg || window
//将this赋给thisArg的fn属性
//此处this指代调用myCall的function
thisArg.fn = this;
var result = thisArg.fn(args);
delete thisArg.fn;
return result;
}
PChain.nameprint.myCall(newname); //pChainChild
3. apply的应用与实现
apply与call的作用是一样的,不过二者接收参数的方法不一样,apply要求将call的剩余参数存储在一个数组中。
当明确知道参数数量时用 call ,而不确定的时候用 apply,并将把参数 push 进数组传递进去,使函数内部也通过 arguments 这个数组来遍历所有的参数。
常用方法:
function pChain2 (name1,name2){
this.name1=name1;
this.name2=name2;
this.nameprint=function(){
console.log('打印的名字是',this.name1+'与'+this.name2);
}
}
function pChainChild2 (){
this.name1="PChainChild1";
this.name2="PChainChild2";
var PChain2 = new pChain2("pChainRoot1","pChainPoot2");
//call的传参方法
PChain2.nameprint.call(this,name1,name2); //PChainChild1与PChainChild2
PChain2.nameprint(); //pChainRoot1与pChainPoot2
//apply的传参方法
PChain2.nameprint.apply(this,[name1,name2]); //PChainChild1与PChainChild2
}
pChainChild2();
模拟实现:
Function.prototype.myApply = function(thisArg){
//thisArg为调用mycall方法的函数的this指向
var thisArg = thisArg || window
//将this赋给thisArg的fn属性
//此处this指代调用myCall的function
thisArg.fn = this;
var result;
if (arguments[1]) {
result = thisArg.fn(arguments[1]);
} else {
result = thisArg.fn();
}
delete thisArg.fn;
return result;
}
4. bind的应用与实现
bind是在es5中扩展的方法(IE6,7,8不支持),但是返回值是函数。
常用方法:
var bar= function () {
console.log(this.x);
}
var foo = {
x:3
}
bar(); //undefined
bar.bind (foo) (); //3
要注意的是多次 bind() 是无效的。
var foo2 = {
x:5
}
bar.bind (foo).bind (foo2) (); //3
模拟实现:
Function.prototype.myBind=function(){
var _this = this;
var context = [].shift.call(arguments);// 保存需要绑定的this上下文
var args = [].slice.call(arguments); //剩下参数转为数组
return function(){
_this.apply(context, [].concat.call(args, [].slice.call(arguments)));
}
}
5. new的应用与实现
在学习了原型与原型链 -> 12.29part2 之后,我们知道new在构建过程中,会发生的步骤为:
- 生成新的对象
- 新对象的__proto__属性指向构造函数的原型对象
- 绑定this,将构造函数的作用域赋值给新对象
- 返回新对象
因此手动模拟new关键字只需要实现上述四个步骤即可
function myNew(fn, ...args) {
const obj = {};
//绑定原型链
obj.__proto__ = fn.prototype;
// 改变this指向
let result = fn.apply(obj, args)
// return result
// 确保new出来的是一个对象
return typeof result === "object" ? result : obj
}