JS中bind,call,apply以及new的用法与实现

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
    }
posted @ 2021-01-19 14:34  凉将  阅读(368)  评论(0编辑  收藏  举报