zoukankan      html  css  js  c++  java
  • 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;
    
        }
    
    
  • 相关阅读:
    10.13 新版本go on~
    9.30 总结一下九月呗
    9.25 学习下日期加减
    9.22 迎难而上不要怂!
    9.22 Sans-serif VS Serif
    9.22 keep studying
    【LeetCode刷题】最长同值路径:妙解
    【LeetCode刷题】机器人走路最大距离:妙解
    【LeetCode刷题】不使用+-的加减法:妙解
    【LeetCode刷题】NIM游戏:妙解
  • 原文地址:https://www.cnblogs.com/yalong/p/11209939.html
Copyright © 2011-2022 走看看