js中instanceof Object.create Funtion.prototype.call方法的底层实现

742 阅读6分钟

1. instansof 方法

instanceof 功能介绍

**功能1:**instanceof用于判断一个变量是否某个对象的实例。

更深层次的是:a instanceof Foo ,instanceof 操作符的左操作符是一个普通的对象,右操作数是一个函数。 instanceof回答的问题是: 在a的整条[[Prototype]]链中是否有Foo.prototype指向的对象。

var a=new Array();    console.log(a instanceof Array);会输出true,

同时console.log(a instanceof Object)也会输出true;这是因为Array是object的子类。

再如:function test(){};  var a=new test();   console.log(a instanceof test)会输出true。

功能2:  instanceof 判断数据类型 ,其实就是判断在a的整条[[Prototype]]链中是否有Foo.prototype指向的对象。

如:let str = new String( '麻烦各位点点赞');

str instanceof String  // true

instanceof 底层实现

1.  instanceof 最终实现代码如下:

**第一种:
**

function instance_of(L,R) {   
    var O = R.prototype; // 构造函数的原型对象      
    L = L.__proto__;    
    while(true){        
        if(L === null) return false;        
        if(L === O){            
            return true        
        }        
    L = L.__proto__;    
    }
}

第二种:

function myInstanceof(L, R) {    
    // 这里先用typeof来判断基础数据类型,如果是,直接返回false    
    if(typeof L !== 'object' || L === null) return false;    
    // getProtypeOf是Object对象自带的API,能够拿到参数的原型对象    
    let proto = Object.getPrototypeOf(L);    
    while(true) {                  //循环往下寻找,直到找到相同的原型对象      
        if(proto === null) return false;      
        if(proto === R.prototype) return true;//找到相同原型对象,返回true      
        proto = Object.getPrototypeof(proto);      
    }  
}

2. 上述代码实现的历程:

由于instenceof使用了原型方面的知识,所以直接上一个经典原型学习图。

       

  •  每一个实例(例如 f1 和 f2)都有一个"__proto__"指向Foo.prototype。
  • 评判依据:  面对function test(){}; var a=new test(); 当a.__proto__ === text.prototype,则f1 instanceof test 就会返回true。

初级instanceof 的实现代码为:

function instance_of(L,R) {    
    var O = R.prototype; // 构造函数的原型对象    
    if(L.__proto__ === O){    
        return true;    
    }
    else{
        return false; 
    }
}

不足: 

instanceof  要搞定的是:在a的整条[[Prototype]]链中是否有Foo.prototype指向的对象。

而面对于a instanceof Object就没法子的返回true了,所以根据经典图,知道一直有"__proto__"属性指到null,所以弄个循环就可以搞定了。

while(true){        
    if(L === null)
        return false;        
    if(L === O){           
        return true       
    }        
    L = L.__proto__;    
}

L = L.__proto__为null时循环会退出。

由于instanceof判断数据类型有弊端:

实现一个全局通用的数据类型判断方法,代码如下:

function getType(obj){  
    let type  = typeof obj;  
    if (type !== "object") {    
    // 先进行typeof判断,如果是基础数据类型,直接返回    
    return type;  
    }  
    // 对于typeof返回结果是object的,再进行如下的判断,正则返回结果  
    return Object.prototype.toString.call(obj).replace(/^\[object (\S+)\]$/, '$1');  
    // 注意正则中间有个空格
}

2.Object.create 方法

Object.create 功能介绍

  1. 语法:
    Object.create(proto, [propertiesObject])
    //方法创建一个新对象,使用现有的对象来提供新创建的对象的proto
  2. 参数:
  • proto : 必须。表示新建对象的原型对象,即该参数会被赋值到目标对象(即新对象,或说是最后返回的对象)的原型上。该参数可以是null对象, 函数的prototype属性 (创建空的对象时需传null , 否则会抛出TypeError异常)。
  • propertiesObject : 可选。
  • 返回值:在指定原型对象上添加新属性后的对象。

const me = Object_create(person);代码意思为:调用Object.create()会凭空创建一个"新"对象并把新对象内部的[[Prototype]]关联到你的指定对象(即为"person");

Object.create 底层实现

**1. 代码实现依据:**调用Object.create()会凭空创建一个"新"对象并把新对象内部的[[Prototype]]关联到你的指定对象。

实现代码如下:

function Object_create(proto) {    
    function F() {}    
    F.prototype = proto;    
    return new F();
}

验证代码:

const person = { 
isHuman: false,
    printIntroduction: function() {
    console.log('My name is ${this.name} .Am I human ? ${this.isHuman}');
}
}
const me = Object_create(person);
console.log(me.isHuman);
console.log(me.__proto__);
console.log(me.__proto__ === person);

终端打印:

2. 上述验证代码实现的历程:

创建一个新的构造函数F(),让F.prototype指向proto。

由const me = Object_create(person); 得到me.__peoto__ ===person。

me.__peoto__ === (Me.prototype) === person。实现了继承。

3 Funtion.prototype.call 方法

Funtion.prototype.call功能介绍

**call()** 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。

1.使用 call 方法调用函数并且指定上下文的 'this'

例如: foo.call.(obj),可以在调用foo时强制把foo.this绑定到obj。

2.使用 call 方法调用父构造函数,

function Aniaml(name) {
  this.name = name;
}

function Cat(name, price) {
  Product.call(this, name);
  this.food = 'fish';
}

语法: function.call(thisArg, arg1, arg2, ...)

Funtion.prototype.call 底层实现:

1. 代码实现如下:

Function.prototype.Mycall = function (obj, ...rest) {       
    var obj = obj || window;       
    obj._fn = this;        
    var res = eval('obj._fn(...rest)');        
    delete obj._fn        
    return res;        
}

2. 上述代码实现的历程:

如果要了解Funtion.prototype.call的底层机制,得了解词法作用域。欢迎观看大佬的juejin.cn/post/692423…

明白call实现的真正的东西:foo.call.(obj),可以在调用foo时强制把foo.this绑定到obj。

1.来看这一段代码

var a = 1;       
function test() {            
    console.log(this === window);           
    console.log(this.a);        
    }        
test();

输出为:

                                     

说明: this已经绑定了全局对象

2.来看这一坨代码

var testObj = {            
    a:3,            
    test: function() {                
        console.log(this === testObj,'判断');                
        console.log(this.a);            
        }        
    }        
testObj.test();

输出为:

                                        

说明:this已经绑定了testObj

3.来看这一堆代码

function test() {            
    console.log(this === obj);            
    console.log(this.a,'obj');        
}        
var obj = {            
    a:2        
}        
test.call(obj);

输出为:

                                   

**说明:**this已经绑定了obj

对于1, 2 ,3的代码进行总结:

  • 函数作为 普通函数调用的时候 this === window
  • 函数作为 对象的属性 调用的时候 this === 对象的属性
  • 函数用 call 来调用 this === call 第一个参数

4. 由于是通过"."进行调用call,所以使用Foo.prototype.Mycall进行扩展。

Function.prototype.myCall = function () {           
     console.log("欢迎来到Mycall的世界!")       
}        
function testMycall() {   }               
 testMycall.myCall();

                               

所有借用"函数作为 对象的属性 调用的时候 this === 对象的属性",来实现.call。

代码如下:

function testMycall() {           
    console.log(this.b);        
}        
var testObj1 = {            
    b: 'b val',            
    _fn:testMycall        
}        
testObj1._fn();  // this === testObj1

在testObj1中定义一个_fn:testMycall,然后调用testObj1.fn(),实现了this绑定到了testObj1。

实现了类似testMycall.call(testObj1)的功能。

所以:testObj1._fn = testMycall,要获取"testMycall",就用"this"(this === testMycall)

Funtion.prototype.call实现代码版本1:

Function.prototype.myCall = function (obj) {           
    obj._fn = this;
    obj._fn();        
}

6. .myCall要进行传值

Funtion.prototype.call实现代码版本2:

Function.prototype.myCall = function (obj,...rest) {            
    obj._fn = this;            
    obj._fn(...rest);        
}        
function testMycall(a,c,d) {            
    console.log(this.b + a + c +d);        
}        
var testObj1 = {            
    b: 'b val',            
    _fn:testMycall        
}        t
estMycall.myCall(testObj1,'   a val','   c val','   d val');

7.function testMycall() {}中以return返回,则.myCall要return

8. 要删除._fn()这个属性。

                    

Funtion.prototype.call实现代码版本3:

Function.prototype.myCall = function (obj,...rest) {            
    obj._fn = this;            
    let res = obj._fn(...rest);            
    delete obj._fn;            
    return res;        
}

9.加个eval:

eval函数接收一个参数string,如果string不是字符串,则直接返回string。否则执行string语句。如果string语句执行结果是一个值,则返回此值,否则返回undefined。

最后就变成了最终代码。

与Function.prototype.call相关的功能实现

.apply的底层实现:

.apply只能传作为一个数组(或类似数组对象)提供的参数

Function.prototype.apply = function (obj, rest) {            
    var obj = obj || window;            
    obj._fn = this;            
    var res = eval('obj._fn(...rest)');            
    delete obj._fn            
    return res;        
}

.bind的底层实现:

Function.prototype.bind = function (obj, ...rest) {        
    if (typeof this !== "function") {            
    throw new Error("this must be a function");        
    }        
    var self = this;        
    var fbound = function () {            
        self.apply(this instanceof self ? this : obj, rest.concat(Array.prototype.slice.call(arguments)));
    }        
    if(this.prototype) {            
        fbound.prototype = Object.create(this.prototype);        
    }            
    return fbound;        
}

new的底层实现:

function _new(ctor, ...args) {        
    if(typeof ctor !== 'function') {        
        throw 'ctor must be a function';        
    }        
    let obj = new Object();        
    obj.__proto__ = Object.create(ctor.prototype);        
    let res = ctor.apply(obj,  [...args]);        
    let isObject = typeof res === 'object' && typeof res !== null;        
    let isFunction = typeof res === 'function';        
    return isObject || isFunction ? res : obj;        
};

总结:

各位观众大老爷,该文有何不足,望 指出喔!

祝各位新年快乐,万事如意,一年比一年好。

最后来张图