js 中 new call apply bind JSON.stringify 的原理以及模拟实现

 1.new的原理和实现

  • 它创建了一个全新的对象。

  • 它会被执行 [[Prototype]](也就是 __proto__)链接。

  • 它使 this指向新创建的对象。

  • 通过 new创建的每个对象将最终被 [[Prototype]]链接到这个函数的 prototype对象上。

  • 如果函数没有返回对象类型 Object(包含 Functoin,Array,Date,RegExg,Error),那么 new表达式中的函数调用将返回该对象引用。

   var A = function(x, y){
        this.x = x;
        this.y = y;
        this.add = function(){
            console.log(this.x + this.y)
        }
    }
    A.prototype.sayHello = function(){
        console.log('hello !')
    }

    function New(func) {
        var res = {}
        if(func.prototype !== null){
            res.__proto__ = func.prototype;
        }
        // Array.prototype.slice.call(arguments, 1) 是获取参数 arguments[0]是 func 去掉不用
        
// 更多argument的更多说明看这个: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/arguments
// 用apply 不用call 是因为 Array.prototype.slice.call(arguments, 1) 的结果是一个数组。
// 执行一次func的原因是要让 A 函数执行初始化 简单来说就是给 x y add 赋值
var ret = func.apply(res, Array.prototype.slice.call(arguments, 1));
     if((typeof ret === 'object' || typeof ret === 'function') && ret !== null) { return ret; }else { return res; } } var obj = New(A, 1, 2) // 或者 var obj = new A(1,2)

2.JSON.stringify的原理和实现

  • Boolean|Number|String 类型会自动转换成对应的原始值。

  • undefined、任意函数以及 symbol,会被忽略(出现在非数组对象的属性值中时),或者被转换成 null(出现在数组中时)。

  • 不可枚举的属性会被忽略

     平常用的用 JSON.parse(JSON.stringify(obj)) 实现深度克隆的时候, value 是 undefined、函数以及 symbol的 key 都会消失,原因就是上面第二条

 

    // 对于元素是数组的情况还没做处理,有兴趣的可自己试试
    function json2str(o) {
        let arr = [];
        const fmt = function(s) {
            if(typeof s == 'object' && s !== null){
                return json2str(s);
            }
            // undefine symbol function的时候 设置为空, 注意区分 '' 与 `"${s}"`, 后者多了 "", 不是真正的空
            if(s === null) {
                return null
            } else if (s === false) {
                return false
            } else if(/^(undefined|symbol|function)$/.test(typeof s)) {
                return ''
            } else {
                return `"${s}"`
            }
        }
        for (var i in o) {
            // 如果是空 就代表是 undefine symbol function  就不用了,去掉
            if(fmt(o[i]) === null || fmt(o[i]) === false || fmt(o[i])){
                arr.push(`"${i}":${fmt(o[i])}`)
            }
        }
        return `{${arr.join(',')}}`
    }

    var obj = {
        name: '唐伯龙', 
        age: '20', 
        msg: {
            title:'11',
            face:'22'
        },
        a: function func(){alert('1')}, 
        b: false, 
        c: null, 
        d: undefined, e: Symbol(),
        e: ''
    }
   // 下面两个打印的结果是一样的 console.log(JSON.stringify(obj)) console.log(json2str(obj))
console.log(JSON.stringify(obj)) 与 console.log(json2str(obj))的结果如下,一模一样:

3.call apply 的原理和实现

更详细的解释看这里 https://www.jianshu.com/p/af3f41d8ef99

call语法:

fun.call(thisArg,arg1,arg2,...),调用一个函数, 其具有一个指定的this值和分别地提供的参数(参数的列表)。

apply语法:

func.apply(thisArg,[argsArray]),调用一个函数,以及作为一个数组(或类似数组对象)提供的参数。

call 代码实现如下:

    Function.prototype.call2 = function (context) {
        var context = context || window; // 当context是null 的时候 this 指向window
        context.fn = this;

        var args = [...arguments].slice(1);

        var result = context.fn(args);

        delete context.fn
        return result;
    }

    // 测试一下
    var value = 2;

    var obj = {
        value: 1
    }

    function bar(name, age) {
        console.log(this.value);
        return {
            value: this.value,
            name: name,
            age: age
        }
    }

    bar.call(null); // 当传入null的时候 this指向window, 所以输出 2

    console.log(bar.call2(obj, 'Cherry', 18));

apply 代码实现如下 (其实就是传入的第二个参数变成了一个数组) :

    Function.prototype.call2 = function (context, arr) {
        var context = context || window; // 当context是null 的时候 this 指向window
        context.fn = this;
        var result = ''
        if(!arr) {
            result = context.fn()
        } else {
            var args = []; 
            for(var i = 0; i < arr.length; i++) {
                 args.push('arr[' + i + ']');
             } 
             result =  eval('obj.fn('+args+')');
        }
        delete context.fn
        return result;
    }

    // 测试一下
    var value = 2;

    var obj = {
        value: 1
    }

    function bar(name, age) {
        console.log(this.value);
        return {
            value: this.value,
            name: name,
            age: age
        }
    }

    bar.call(null); // 当传入null的时候 this指向window, 

    console.log(bar.call2(obj, ['Cherry', 18]));

4.bind 的原理和实现

更详细的说明看这里: https://blog.csdn.net/daimomo000/article/details/72897035

bind()方法:

会创建一个新函数。当这个新函数被调用时,bind() 的第一个参数将作为它运行时的 this,之后的一序列参数将会在传递的实参前传入作为它的参数。(来自于 MDN )

    Function.prototype.bind2 = function (context) {

        if (typeof this !== "function") {
          throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
        }

        var self = this;
        var args = Array.prototype.slice.call(arguments, 1);
        var fNOP = function () {};

        var fbound = function () {
            self.apply(this instanceof self ? this : context, args.concat(Array.prototype.slice.call(arguments)));
        }

        fNOP.prototype = this.prototype;
        fbound.prototype = new fNOP();

        return fbound;

    }

 

 

 


posted @ 2019-07-18 21:03  进军的蜗牛  阅读(605)  评论(1编辑  收藏  举报