您的位置:新葡亰496net > 新葡亰官网 > 新葡亰496net增加生产数量知识点总括,观念的落

新葡亰496net增加生产数量知识点总括,观念的落

发布时间:2019-11-16 08:38编辑:新葡亰官网浏览(81)

    Mobx 思想的实现原理

    2017/03/11 · JavaScript · mobx, React, vuejs, 前端

    本文作者: 伯乐在线 - ascoders 。未经作者许可,禁止转载!
    欢迎加入伯乐在线 专栏作者。

    Mobx 最关键的函数在于 autoRun,举个例子,它可以达到这样的效果:

    const obj = observable({ a: 1, b: 2 }) autoRun(() => { console.log(obj.a) }) obj.b = 3 // 什么都没有发生 obj.a = 2 // observe 函数的回调触发了,控制台输出:2

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    const obj = observable({
        a: 1,
        b: 2
    })
     
    autoRun(() => {
        console.log(obj.a)
    })
     
    obj.b = 3 // 什么都没有发生
    obj.a = 2 // observe 函数的回调触发了,控制台输出:2

    我们发现这个函数非常智能,用到了什么属性,就会和这个属性挂上钩,从此一旦这个属性发生了改变,就会触发回调,通知你可以拿到新值了。没有用到的属性,无论你怎么修改,它都不会触发回调,这就是神奇的地方。

    defineProperty()

    本人自学es6已经有一段时间了,只觉得有些时候很是枯燥无味, 时而又觉得在以后的职业生涯中会很有用,因为es6的很多方法和集成的定义和方法会在开发中遇到的越来越多,近期本人在学Reflect,把自己的心得以及体会分享给大家,希望能对你们有用。

    ECMAScript发展历史

    (1)ECMA-262 第1版:去除了对针对浏览器的特性,支持Unicode标准(多语言开发),对象也变成了和平台无关的。(1997年)
    (2)ECMA-262 第2版:没有增删改,就是编辑加工工作。(1998年6月)
    (3)第三版ECMAScript3新增了对正则表达式、新控制语句、try-catch异常处理的支持,修改了字符处理、错误定义和数值输出等内容。标志着ECMAScript成为了一门真正的编程语言。(1999年12月)
    (4)第四版于2008年7月发布前被废弃, 但是它的大部分内容被ES6继承了。
    (5)第五版ECMAScript5力求澄清第3版中的歧义,并添加了新的功能。新功能包括:原生JSON对象、继承的方法、高级属性的定义以及引入严格模式。
    (6)第六版ECMAScript6是继ES5之后的一次主要改进,增添了许多必要的特性,例如:模块和类以及一些实用特性,Maps、Sets、Promises、生成器(Generators)等。

    ##Proxy

    autoRun 的用途

    使用 autoRun 实现 mobx-react 非常简单,核心思想是将组件外面包上 autoRun,这样代码中用到的所有属性都会像上面 Demo 一样,与当前组件绑定,一旦任何值发生了修改,就直接 forceUpdate,而且精确命中,效率最高。

    学习书籍《ECMAScript 6 入门 》

    // Reflect对象与proxy对象一样,也是为了操作对象而提供的新Api

    ECMAScript5部分新增特性简介

    ###简单理解

    依赖收集

    autoRun 的专业名词叫做依赖收集,也就是通过自然的使用,来收集依赖,当变量改变时,根据收集的依赖来判断是否需要更新。

    Proxy


    Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程。

    Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。

    Proxy 构造函数

    var proxy = new Proxy(target,handler);

    Proxy 对象的所有用法,都是上面这种形式,不同的只是handler参数的写法。其中,new Proxy()表示生成一个Proxy实例,target参数表示所要拦截的目标对象,handler参数也是一个对象,用来定制拦截行为。

    实例代码:

    var handler = {

        get: function(target, name) {

            if (name === 'prototype') {

                return Object.prototype;

            }

            return 'Hello, ' name;

        },

        apply: function(target, thisBinding, args) {

            return args[0];

        },

       construct: function(target, args) {

            return {value: args[1]};

        }

    };

    var fproxy = new Proxy(function(x, y) {

        return x y;

      }, handler);

    fproxy(1, 2)  // 1

    new fproxy(1,2) // {value: 2}

    fproxy.prototype === Object.prototype // true

    fproxy.foo // "Hello, foo"

    下面是 Proxy 支持的拦截操作一览。

    (1)get(target, propKey, receiver)

    拦截对象属性的读取,比如proxy.fooproxy['foo']

    最后一个参数receiver是一个对象,可选,参见下面Reflect.get的部分。

    (2)set(target, propKey, value, receiver)

    拦截对象属性的设置,比如proxy.foo = vproxy['foo'] = v,返回一个布尔值。

    (3)has(target, propKey)

    拦截propKey in proxy的操作,返回一个布尔值。

    (4)deleteProperty(target, propKey)

    拦截delete proxy[propKey]的操作,返回一个布尔值。

    (5)ownKeys(target)

    拦截Object.getOwnPropertyNames(proxy)Object.getOwnPropertySymbols(proxy)Object.keys(proxy),返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()的返回结果仅包括目标对象自身的可遍历属性。

    (6)getOwnPropertyDescriptor(target, propKey)

    拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。

    (7)defineProperty(target, propKey, propDesc)

    拦截Object.defineProperty(proxy, propKey, propDesc)Object.defineProperties(proxy, propDescs),返回一个布尔值。

    (8)preventExtensions(target)

    拦截Object.preventExtensions(proxy),返回一个布尔值。

    (9)getPrototypeOf(target)

    拦截Object.getPrototypeOf(proxy),返回一个对象。

    (10)isExtensible(target)

    拦截Object.isExtensible(proxy),返回一个布尔值。

    (11)setPrototypeOf(target, proto)

    拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。

    如果目标对象是函数,那么还有两种额外操作可以拦截。

    (12)apply(target, object, args)

    拦截 Proxy 实例作为函数调用的操作,比如proxy(...args)proxy.call(object, ...args)*proxy.apply(...)。*

    (13)construct(target, args)

    拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)

    get()

    下面的例子使用get拦截,实现数组读取负数的索引。


    function createArray(...elements) {

        let handler = {

            get(target, propKey, receiver) {

                let index = Number(propKey);

                if (index < 0) {

                    propKey = String(target.length index);

                }

                 return Reflect.get(target, propKey, receiver);

            }

        };

        let target = [];

        target.push(...elements);

        return new Proxy(target, handler);

    }

    let arr = createArray('a', 'b', 'c');

    arr[-1] // c

    该例子,通过判定index来对数组的角标返回值进行拦截过滤

    if(index<0){

        propKey=String(target.length index);

    }

    即当index小于0时,propkey也就是 arr[-1]  传入的-1的index,对其进行修改,让 propkey的值变为 (target.length index)= 3 (-1)=2;

    然后执行反射函数 Reflect;

    Reflect.get(target, propKey, receiver)

    通过字面意思翻译以及上面代码的返回值 可猜测到  Refect为反射函数;

    而该句的意思为:用过Reflect 调用 拦截对象 本身的get方法执行;


    利用 Proxy,可以将读取属性的操作(get),转变为执行某个函数,从而实现属性的链式操作。


    var pipe = (function () {

        return function (value) {

            var funcStack = [];

           var oproxy = new Proxy({} , {

                get : function (pipeObject, fnName) {

                    if (fnName === 'get') {

                       return funcStack.reduce(function (val, fn) {

                            return fn(val);

                       },value);

                     }

                    funcStack.push(window[fnName]);

                    return oproxy;

              }

           });

            return oproxy;

          }

        }());

    var double = n => n * 2;

    var pow    = n => n * n;

    var reverseInt = n => n.toString().split("").reverse().join("") | 0;

    pipe(3).double.pow.reverseInt.get; // 63

    该例子用到了一个没见过的方法也就是reduce();通过百度知道了 这是Array 数组里面的一个方法;

    reduce()对数组中的所有元素调用指定的回调函数。该回调函数的返回值为累积结果,并且此返回值在下一次调用该回调函数时作为参数提供。

    新葡亰496net 1

    reduce()

    也就是说reduce方法的第一个参数是回调函数,函数格式如下

    新葡亰496net 2

    reduce方法的回调函数

    而回调函数的第一个参数为上次回调的值,如果首次调用该函数,则参数的值为initalValue.

    所以

    funcStack.reduce(function (val, fn) {

    return fn(val);

    },value);

    该行代码的执行结果为: 

      第一次调用  val = value = 3  ;  fn =  n => n * 2  ;   return 值 : 3*2 = 6;

      第二次调用  val = 6 ; value = 3  ;  fn =  n => n * n ;  return 值 : 6*6 = 36;

      第三次调用  val = 36 ; value = 3  ;  fn =  n =>n.toString().split("").reverse().join("") | 0  ;  

                           return 值 :把36变成字符串 然后分割 反转 变成 '63' ,在转换成 数字  63;

    因此,最后的执行结果为 63 .


    set()

    set方法用来拦截某个属性的赋值操作。

    假定Person对象有一个age属性,该属性应该是一个不大于200的整数,那么可以使用Proxy保证age的属性值符合要求。

    let validator = {

        set: function(obj, prop, value) {

            if (prop === 'age') {

               if (!Number.isInteger(value)) {

                   throw new TypeError('The age is not an integer');

               }

               if (value > 200) {

                 throw new RangeError('The age seems invalid');

               }

            }

            // 对于age以外的属性,直接保存

            obj[prop] = value;

          }

      };

    let person = new Proxy({}, validator);

    person.age = 100;

    person.age // 100

    person.age = 'young' // 报错

    person.age = 300 // 报错


    apply()

    apply方法拦截函数的调用、callapply操作。

    apply方法可以接受三个参数,分别是目标对象、目标对象的上下文对象(this)和目标对象的参数数组。

    var handler = {

        apply (target, ctx, args) {

            return Reflect.apply(...arguments);

        }

    };

    下面是一个例子。

    var target = function () {  return 'I am the target';  };

    var handler = {

        apply: function () {

            return 'I am the proxy';

        }

    };

    var p = new Proxy(target, handler);

    p()

    // "I am the proxy"

    上面代码中,变量p是 Proxy 的实例,当它作为函数调用时(p()),就会被apply方法拦截,返回一个字符串。

    下面是另外一个例子。

    var twice = {

        apply (target, ctx, args) {

            return Reflect.apply(...arguments) * 2;

        }

    };

    function sum (left, right) {

        return left right;

    };

    var proxy = new Proxy(sum, twice);

    proxy(1, 2) // 6

    proxy.call(null, 5, 6) // 22

    proxy.apply(null, [7, 8]) // 30

    上面代码中,每当执行proxy函数(直接调用或call和apply调用),就会被apply方法拦截。

    另外,直接调用Reflect.apply方法,也会被拦截。

    Reflect.apply(proxy,null,[9,10])// 38


    has()

    has方法用来拦截HasProperty操作,即判断对象是否具有某个属性时,这个方法会生效。典型的操作就是in运算符。

    下面的例子使用has方法隐藏某些属性,不被in运算符发现。

    var handler = {

        has (target, key) {

            if (key[0] === '_') {

                return false;

            }

           return key in target;

        }

    };

    var target = { _prop: 'foo', prop: 'foo' };

    var proxy = new Proxy(target, handler);

    '_prop' in proxy // false

    上面代码中,如果原对象的属性名的第一个字符是下划线,proxy.has就会返回false,从而不会被in运算符发现。

    如果原对象不可配置或者禁止扩展,这时has拦截会报错。

    var obj = { a: 10 };

    Object.preventExtensions(obj);

    var p = new Proxy(obj, {

        has: function(target, prop) {

            return false;

        }

    });

    'a' in p // TypeError is thrown

    上面代码中,obj对象禁止扩展,结果使用has拦截就会报错。也就是说,如果某个属性不可配置(或者目标对象不可扩展),则has方法就不得“隐藏”(即返回false)目标对象的该属性。

    值得注意的是,has方法拦截的是HasProperty操作,而不是HasOwnProperty操作,即has方法不判断一个属性是对象自身的属性,还是继承的属性。

    另外,虽然for...in循环也用到了in运算符,但是has拦截对for...in循环不生效。

    let stu1 = {name: '张三', score: 59};

    let stu2 = {name: '李四', score: 99};

    let handler = {

        has(target, prop) {

            if (prop === 'score' && target[prop] < 60) {

                console.log(`${target.name} 不及格`);

                return false;

            }

            return prop in target;

        }

    };

    let oproxy1 = new Proxy(stu1, handler);

    let oproxy2 = new Proxy(stu2, handler);

    'score' in oproxy1

    // 张三 不及格

    // false

    'score' in oproxy2

    // true

    for (let a in oproxy1) {

    console.log(oproxy1[a]);

    }

    // 张三

    // 59

    for (let b in oproxy2) {

    console.log(oproxy2[b]);

    }

    // 李四

    // 99

    上面代码中,has拦截只对in循环生效,对for...in循环不生效,导致不符合要求的属性没有被排除在for...in循环之外。


    construct()

    construct方法用于拦截new命令,下面是拦截对象的写法。

    var handler = {

        construct (target, args, newTarget) {

            return new target(...args);

        }

    };

    construct方法返回的必须是一个对象,否则会报错。

    construct方法可以接受两个参数。

        --target: 目标对象

        --args:构建函数的参数对象

    var p = new Proxy(function () {}, {

        construct: function(target, args) {

            console.log('called: ' args.join(', '));

            return { value: args[0] * 10 };

        }

    });

    (new p(1)).value

    // "called: 1"

    // 10


    deleteProperty()

    deleteProperty方法用于拦截delete操作,如果这个方法抛出错误或者返回false,当前属性就无法被delete命令删除。

    var handler = {

        deleteProperty (target, key) {

            invariant(key, 'delete');

            return true;

        }

    };

    function invariant (key, action) {

        if (key[0] === '_') {

            throw new Error(`Invalid attempt to ${action} private "${key}" property`);

        }

    新葡亰496net增加生产数量知识点总括,观念的落到实处原理。}

    var target = { _prop: 'foo' };

    var proxy = new Proxy(target, handler);

    delete proxy._prop

    // Error: Invalid attempt to delete private "_prop" property

    上面代码中,deleteProperty方法拦截了delete操作符,删除第一个字符为下划线的属性会报错。

    注意,目标对象自身的不可配置(configurable)的属性,不能被deleteProperty方法删除,否则报错。


    defineProperty()

    defineProperty方法拦截了Object.defineProperty操作。

    var handler = {

        defineProperty (target, key, descriptor) {

            return false;

        }

    };

    var target = {};

    var proxy = new Proxy(target, handler);

    proxy.foo = 'bar'

    // TypeError: proxy defineProperty handler returned false for property '"foo"'

    上面代码中,defineProperty方法返回false,导致添加新属性会抛出错误。

    注意,如果目标对象不可扩展(extensible),则defineProperty不能增加目标对象上不存在的属性,否则会报错。另外,如果目标对象的某个属性不可写(writable)或不可配置(configurable),则defineProperty方法不得改变这两个设置。


    getOwnPropertyDescriptor()

    getOwnPropertyDescriptor方法拦截Object.getOwnPropertyDescriptor*,返回一个属性描述对象或者*undefined。

    var handler = {

        getOwnPropertyDescriptor (target, key) {

            if (key[0] === '_') {

            return;

            }

        return Object.getOwnPropertyDescriptor(target, key);

       }

    };

    var target = { _foo: 'bar', baz: 'tar' };

    var proxy = new Proxy(target, handler);

    Object.getOwnPropertyDescriptor(proxy, 'wat')

    // undefined

    Object.getOwnPropertyDescriptor(proxy, '_foo')

    // undefined

    Object.getOwnPropertyDescriptor(proxy, 'baz')

    // { value: 'tar', writable: true, enumerable: true, configurable: true }

    上面代码中,handler.getOwnPropertyDescriptor方法对于第一个字符为下划线的属性名会返回undefined


    getPrototypeOf()

    getPrototypeOf方法主要用来拦截Object.getPrototypeOf()运算符,以及其他一些操作。

        --Object.prototype.__proto__

        --Object.prototype.isPrototypeOf()

        --Object.getPrototypeOf()

        --Reflect.getPrototypeOf()

        --instanceof运算符

    var proto = {};

    var p = new Proxy({}, {

        getPrototypeOf(target) {

            return proto;

        }

    });

    Object.getPrototypeOf(p) === proto // true

    上面代码中,getPrototypeOf方法拦截Object.getPrototypeOf(),返回proto对象。

    注意,getPrototypeOf方法的返回值必须是对象或者null,否则报错。另外,如果目标对象不可扩展(extensible),getPrototypeOf方法必须返回目标对象的原型对象。


    isExtensible() 

    isExtensible方法拦截Object.isExtensible操作。

    var p = new Proxy({}, {

        isExtensible: function(target) {

            console.log("called");

            return true;

        }

    });

    Object.isExtensible(p)

    // "called"

    // true

    上面代码设置了isExtensible方法,在调用Object.isExtensible时会输出called

    注意,该方法只能返回布尔值,否则返回值会被自动转为布尔值。

    这个方法有一个强限制,它的返回值必须与目标对象的isExtensible属性保持一致,否则就会抛出错误。


    ownKeys()

    ownKeys方法用来拦截以下操作。

        --Object.getOwnPropertyNames()

        --Object.getOwnPropertySymbols()

        --Object.keys()

    注意,使用Object.keys方法时,有三类属性会被ownKeys方法自动过滤,不会返回。

        --目标对象上不存在的属性

        --属性名为 Symbol 值

        --不可遍历(enumerable)的属性

    let target = {

        a: 1,

        b: 2,

        c: 3,

        [Symbol.for('secret')]: '4',

    };

    Object.defineProperty(target, 'key', {

        enumerable: false,

        configurable: true,

        writable: true,

        value: 'static'

    });

    let handler = {

        ownKeys(target) {

            return ['a', 'd', Symbol.for('secret'), 'key'];

        }

    };

    let proxy = new Proxy(target, handler);

    Object.keys(proxy)

    // ['a']

    上面代码中,ownKeys方法之中,显式返回不存在的属性(d)、Symbol 值(Symbol.for('secret'))、不可遍历的属性(key),结果都被自动过滤掉。


    preventExtensions()

    preventExtensions方法拦截Object.preventExtensions()。该方法必须返回一个布尔值,否则会被自动转为布尔值。

    这个方法有一个限制,只有目标对象不可扩展时(即Object.isExtensible(proxy)false),proxy.preventExtensions才能返回true,否则会报错。

    var p = new Proxy({}, {

        preventExtensions: function(target) {

            return true;

        }

    });

    Object.preventExtensions(p) // 报错

    上面代码中,proxy.preventExtensions方法返回true,但这时Object.isExtensible(proxy)会返回true,因此报错。

    为了防止出现这个问题,通常要在proxy.preventExtensions方法里面,调用一次Object.preventExtensions

    var p = new Proxy({}, {

        preventExtensions: function(target) {

             console.log('called');

            Object.preventExtensions(target);

            return true;

        }

    });

    Object.preventExtensions(p)

    // "called"

    // true


    setPrototypeOf()

    setPrototypeOf方法主要用来拦截Object.setPrototypeOf方法。

    var handler = {

        setPrototypeOf (target, proto) {

            throw new Error('Changing the prototype is forbidden');

       }

    };

    var proto = {};

    var target = function () {};

    var proxy = new Proxy(target, handler);

    Object.setPrototypeOf(proxy, proto);

    // Error: Changing the prototype is forbidden

    上面代码中,只要修改target的原型对象,就会报错。

    注意,该方法只能返回布尔值,否则会被自动转为布尔值。另外,如果目标对象不可扩展(extensible),setPrototypeOf方法不得改变目标对象的原型。


    Proxy.revocable()

    Proxy.revocable方法返回一个可取消的 Proxy 实例。

    let target = {};

    let handler = {};

    let {proxy, revoke} = Proxy.revocable(target, handler);

    proxy.foo = 123;

    proxy.foo // 123

    revoke();

    proxy.foo // TypeError: Revoked

    Proxy.revocable方法返回一个对象,该对象的proxy属性是Proxy实例,revoke属性是一个函数,可以取消Proxy实例。上面代码中,当执行revoke函数之后,再访问Proxy实例,就会抛出一个错误。

    Proxy.revocable的一个使用场景是,目标对象不允许直接访问,必须通过代理访问,一旦访问结束,就收回代理权,不允许再次访问。


    this 问题

    虽然 Proxy 可以代理针对目标对象的访问,但它不是目标对象的透明代理,即不做任何拦截的情况下,也无法保证与目标对象的行为一致。主要原因就是在 Proxy 代理的情况下,目标对象内部的this关键字会指向 Proxy 代理。

    const target = {

        m: function () {

            console.log(this === proxy);

        }

    };

    const handler = {};

    const proxy = new Proxy(target, handler);

    target.m() // false

    proxy.m()  // true

    上面代码中,一旦proxy代理target.m,后者内部的this就是指向proxy,而不是target


    实例:Web 服务的客户端

    Proxy 对象可以拦截目标对象的任意属性,这使得它很合适用来写 Web 服务的客户端。

    const service = createWebService ('');

    service.employees().then(json => {

        const employees = JSON.parse(json);

        // ···

    });

    上面代码新建了一个 Web 服务的接口,这个接口返回各种数据。Proxy 可以拦截这个对象的任意属性,所以不用为每一种数据写一个适配方法,只要写一个 Proxy 拦截就可以了。

    function createWebService(baseUrl) {

        return new Proxy({}, {

            get(target, propKey, receiver) {

             return () => httpGet(baseUrl '/' propKey);

            }

        });

    }

    同理,Proxy 也可以用来实现数据库的 ORM 层。

    // 目的是1.通过Reflect拿到语言内部的方法  2.修改某些Object方法的返回结果,让其变得更合理Object.defineProperty(obj, name, desc)z在无法定义属性时会抛出一个错误。而,Reflect则会返回false

    一、浏览器支持:

    Chrome13 、FireFox4 、Safari 5.1、IE9
    IE9不支持严格模式
    Safari5.1*不支持function.prototype.bind方法

    > 通过第三方代理对目标对象进行增删改查,并能在增删改查行为中添加自定义行为

    实现步骤拆解

    为了兼容,Mobx 使用了 Object.defineProperty 拦截 gettersetter,但是无法拦截未定义的变量,为了方便,我们使用 proxy 来讲解,而且可以监听未定义的变量哦。

     例如:

    二、JSON对象

    JSON.parse(jsonstr) 将json字符串转化成json对象
    JSON.stringify(jsonobj) 将json对象转化成json字符串

    eval() :
    它可以编译执行任何JavaScript程序,因此产生了安全性问题。当使用可信任与完善的源代码时才可以使用eval函数。
    json2.js :
    https://github.com/douglascrockford/JSON-js/blob/master/json2.js

    ###基本结构

    步骤一 存储结构

    众所周知,事件监听是需要预先存储的,autoRun 也一样,为了知道当变量修改后,哪些方法应该被触发,我们需要一个存储结构。

    首先,我们需要存储所有的代理对象,让我们无论拿到原始对象,还是代理对象,都能快速的找出是否有对应的代理对象存在,这个功能用在判断代理是否存在,是否合法,以及同一个对象不会生成两个代理。

    代码如下:

    const proxies = new WeakMap() function isObservable<T extends object>(obj: T) { return (proxies.get(obj) === obj) }

    1
    2
    3
    4
    5
    const proxies = new WeakMap()
     
    function isObservable<T extends object>(obj: T) {
        return (proxies.get(obj) === obj)
    }

    重点来了,第二个要存储的是最重要的部分,也就是所有监听!当任何对象被改变的时候,我们需要知道它每一个 key 对应着哪些监听(这些监听由 autoRun 注册),也就是,最终会存在多个对象,每个对象的每个 key 都可能与多个 autoRun 绑定,这样在更新某个 key 时,直接触发与其绑定的所有 autoRun 即可。

    代码如下:

    const observers = new WeakMap<object, Map<PropertyKey, Set<Observer>>>()

    1
    const observers = new WeakMap<object, Map<PropertyKey, Set<Observer>>>()

    第三个存储结构就是待观察队列,为了使同一个调用栈多次赋值仅执行一次 autoRun,所有待执行的都会放在这个队列中,在下一时刻统一执行队列并清空,执行的时候,当前所有 autoRun 都是在同一时刻触发的,所以让相同的 autoRun 不用触发多次即可实现性能优化。

    const queuedObservers = new Set()

    1
    const queuedObservers = new Set()

    代码如下:

    我们还要再存储两个全局变量,分别是是否在队列执行中,以及当前执行到的 autoRun

    代码如下:

    let queued = false let currentObserver: Observer = null

    1
    2
    let queued = false
    let currentObserver: Observer = null

    老写法

    三、新增Object接口
    对象 构造器 说明
    Object getPrototypeOf 返回对象的原型
    Object GetOwnPropertyDescriptor 返回对象自有属性的属性描述符
    Object getOwnPropertyNames 返回一个数组,包括对象所有自有属性名称集合(包括不可枚举的属性)
    Object create 创建一个拥有置顶原型和若干个指定属性的对象
    Object defineProperty 给对象定义一个新属性,或者修改已有的属性,并返回
    Object defineProperties 在一个对象上添加或修改一个或者多个自有属性,并返回该对象
    Object seal 锁定对象。阻止修改现有属性的特性,并阻止添加新属性。但是可以修改已有属性的值。
    Object freeze 冻结对象,阻止对对象的一切操作。冻结对象将永远不可变。
    Object preventExtensions 让一个对象变的不可扩展,也就是永远不能再添加新的属性。
    Object isSealed 判断对象是否被锁定
    Object isFrozen 判断对象是否被冻结
    Object isExtensible 判断对象是否可以被扩展
    Object keys 返回一个由给定对象的所有可枚举自身属性的属性名组成的数组

    Objec.defineProperty 设置对象属性:
    value, writable, enumerable, configurable

    Object.create 兼容性处理:

    if(!Object.create){
        Object.create = function(proto){
            function F(){};
            F.prototype = proto;
                return new F();
            }
    }
    

    缺点:无法继承“父类”自身的属性
    经典的继承:

    function SuperClass(name){
        this.name = name;
    }
    SuperClass.prototype.sayName = function(){
        alert(this.name);
    }
    
    function SubClass(name,age){
        SuperClass.call(this,name);
        this.age = age;
    }
    SubClass.prototype = SuperClass.prototype;//错误的 在SubClass.prototype上面添加属性的时候,SuperClass.prototype也会被修改。
    SubClass.prototype = new SuperClass(); //不好,不能给SuperClass传参
    SubClass.prototype = Object.create(SuperClass.prototype); //原型链写不向上查找的特性
    SubClass.prototype.constructor = SubClass;
    SubClass.prototype.sayAge = function(){
        alert(this.age);
    }
    
    var sub = new SubClass("nie",19);
    sub.sayAge();
    sub.sayName();
    

    > ES6原生提供构造函数,用来生成Proxy实例

    步骤二 将对象加工可观察

    这一步讲解的是 observable 做了哪些事,首先第一件就是,如果已经存在代理对象了,就直接返回。

    代码如下:

    function observable<T extends object>(obj: T = {} as T): T { return proxies.get(obj) || toObservable(obj) }

    1
    2
    3
    function observable<T extends object>(obj: T = {} as T): T {
        return proxies.get(obj) || toObservable(obj)
    }

    我们继续看 toObservable 函数,它做的事情是,实例化代理,并拦截 get set 等方法。

    我们先看拦截 get 的作用:先拿到当前要获取的值 result,如果这个值在代理中存在,优先返回代理对象,否则返回 result 本身(没有引用关系的基本类型)。

    上面的逻辑只是简单返回取值,并没有注册这一步,我们在 currentObserver 存在时才会给对象当前 key 注册 autoRun,并且如果结果是对象,又不存在已有的代理,就调用自身 toObservable 再递归一遍,所以返回的对象一定是代理。

    registerObserver 函数的作用是将 targetObj -> key -> autoRun 这个链路关系存到 observers 对象中,当对象修改的时候,可以直接找到对应 keyautoRun

    那么 currentObserver 是什么时候赋值的呢?首先,并不是访问到 get 就要注册 registerObserver,必须在 autoRun 里面的才符合要求,所以执行 autoRun 的时候就会将当前回调函数赋值给 currentObserver,保证了在 autoRun 函数内部所有监听对象的 get 拦截器都能访问到 currentObserver。以此类推,其他 autoRun 函数回调函数内部变量 get 拦截器中,currentObserver 也是对应的回调函数。

    代码如下:

    const dynamicObject = new Proxy(obj, { // ... get(target, key, receiver) { const result = Reflect.get(target, key, receiver) // 如果取的值是对象,优先取代理对象 const resultIsObject = typeof result === 'object' && result const existProxy = resultIsObject && proxies.get(result) // 将监听添加到这个 key 上 if (currentObserver) { registerObserver(target, key) if (resultIsObject) { return existProxy || toObservable(result) } } return existProxy || result }), // ... })

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    const dynamicObject = new Proxy(obj, {
        // ...
        get(target, key, receiver) {
            const result = Reflect.get(target, key, receiver)
     
            // 如果取的值是对象,优先取代理对象
            const resultIsObject = typeof result === 'object' && result
            const existProxy = resultIsObject && proxies.get(result)
     
            // 将监听添加到这个 key 上
            if (currentObserver) {
                registerObserver(target, key)
                if (resultIsObject) {
                    return existProxy || toObservable(result)
                }
            }
     
            return existProxy || result
        }),
        // ...
    })

    setter 过程中,如果对象产生了变动,就会触发 queueObservers 函数执行回调函数,这些回调都在 getter 中定义好了,只需要把当前对象,以及修改的 key 传过去,直接触发对应对象,当前 key 所注册的 autoRun 即可。

    代码如下:

    const dynamicObject = new Proxy(obj, { // ... set(target, key, value, receiver) { // 如果改动了 length 属性,或者新值与旧值不同,触发可观察队列任务 if (key === 'length' || value !== Reflect.get(target, key, receiver)) { queueObservers<T>(target, key) } // 如果新值是对象,优先取原始对象 if (typeof value === 'object' && value) { value = value.$raw || value } return Reflect.set(target, key, value, receiver) }, // ... })

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    const dynamicObject = new Proxy(obj, {
        // ...
        set(target, key, value, receiver) {
            // 如果改动了 length 属性,或者新值与旧值不同,触发可观察队列任务
            if (key === 'length' || value !== Reflect.get(target, key, receiver)) {
                queueObservers<T>(target, key)
            }
     
            // 如果新值是对象,优先取原始对象
            if (typeof value === 'object' && value) {
                value = value.$raw || value
            }
     
            return Reflect.set(target, key, value, receiver)
        },
        // ...
    })

    没错,主要逻辑已经全部说完了,新对象之所以可以检测到,是因为 proxyget 会触发,这要多谢 proxy 的强大。

    可能有人问 Object.defineProperty 为什么不行,原因很简单,因为这个函数只能设置某个 keygetter setter~。

    symbol proxy reflect 这三剑客能做的事还有很多很多,这仅仅是实现 Object.observe 而已,还有更强大的功能可以挖掘。

    • symbol拓展
    • reflect拓展

    try{

    四、新增Array接口
    对象 构造器 说明
    Array.prototype indexOf 返回根据给定元素找到的第一个索引值,否则返回-1
    Array.prototype lastIndexOf 方法返回指定元素在数组中的最后一个的索引,如果不存在则返回 -1
    Array.prototype every 测试数组的所有元素是否都通过了指定函数的测试
    Array.prototype some 测试数组中的某些元素是否通过了指定函数的测试
    Array.prototype forEach 让数组的每一项都执行一次给定的函数
    Array.prototype map 返回一个由原数组中的每个元素调用一个指定方法后的返回值组成的新数组
    Array.prototype filter 利用所有通过指定函数测试的元素创建一个新的数组,并返回
    Array.prototype reduce 接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终为一个值
    Array.prototype reduceRight 接受一个函数作为累加器,让每个值(从右到左,亦即从尾到头)缩减为一个值

    另外,还有一个 Array.isArray(),用来判断某一对象是否为数组。(typeof判断的话,返回object,用instanceof判断的话,IE上的返回值不正确)
    低版本浏览器判断数组的方法:

    function isArray(o) {
        return Object.prototype.toString.call(o) ==="[object Array]";
    }
    

    >> `let proxy = new Proxy(target, handler)//target为要拦截的目标对象,handler用来定制拦截行为`

    总结

    es6 真的非常强大,呼吁大家抛弃 ie11,拥抱美好的未来!

    打赏支持我写出更多好文章,谢谢!

    打赏作者

    Object.defineProperty(target, property, attributes)

    五、Function.prototype.bind

    bind()方法会创建一个新函数,称为绑定函数.当调用这个绑定函数时,绑定函数会以创建它时传入bind()方法的第一个参数作为 this,传入 bind() 方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数。
    这个方法可以改变this的指向,为函数自定义 this指针。
    作用和call,apply类似,区别是 bind将会返回一个新的函数,而 call或者 apply并不会返回一个新的函数,它们将会使用新的 this指针直接进行函数调用

    兼容性解决:

    if(!Function.prototype.bind){
        Function.prototype.bind = function(context){
        var self = this;
        var args = Array.prototype.slice.call(arguments);
        return function(){
                self.apply(context,args.slice(1));
            }
        }
    }
    

    ###注意:

    打赏支持我写出更多好文章,谢谢!

    任选一种支付方式

    新葡亰496net 3 新葡亰496net 4

    1 赞 2 收藏 评论

      success

    六、String.prototype.trim()
    String.prototype.trim = function(){
        return this.replace(/^s |s $/g,"");
    }
    

    > 要使得Proxy起作用,必须针对Proxy实例进行操作,而不是针对目标对象进行操作。

    关于作者:ascoders

    新葡亰496net 5

    前端小魔法师 个人主页 · 我的文章 · 7

    新葡亰496net 6

    } catch(e){

    ECMAScript6部分新增特性简介

    一、 let命令和const命令
    1、let命令
    (1)、let命令,用来声明变量。用法和var类似,区别是let声明的变量只在let所在的代码块中起作用。

    if(true){
    let a = 0;
    var b =1; 
    }
    console.log(a) // ReferenceError: a is not defined
    console.log(b) //1
    

    (for循环的计数器很适合用let命令。)

    (2)、let命令不会产生变量提升

    console.log(a); //undefined
    console.log(b);// ReferenceError: b is not defined
    var a= 0;
    let b =1;
    

    (3)、暂时性死区,代码块内若使用了let命令,则在使用let命令声明变量之前,该变量都是不可用的,在语法上称为“暂时性死区”(temporal dead zone)

    var a = 0;
    if(true){
        console.log(a);// ReferenceError: a is not defined
        let a = 0;
    }
    

    在let命令声明变量之前,都是变量的“死区”

    if(true){
        a = 1; // ReferenceError: a is not defined
        console.log(a); // ReferenceError: a is not defined
        let a;
        console.log(a);//undefined
        a= 123;
        console.log(a);//123
    }
    

    (4) let不允许在相同作用域内,重复声明同一个变量

    if(true){
        var a= 0;
        let a = 1;
        console.log(a);
    }// SyntaxError: Identifier 'a' has already been declared
    
    function testFun(arg){
        let arg = 0;
    }
    testFun(1); // SyntaxError: Identifier 'arg' has already been declared
    
    function testFun2(arg){
        let arg = 0;
    }
    testFun2(1);//不报错
    

    (5) 块级作用域的出现,实际上使得获得广泛应用的立即执行匿名函数(IIFE)不再必要了。

    //匿名函数写法 
    (function(){
        var temp = 0;
    })();
    //块级作用域写法
    {
        let temp = 0;
    }
    

    2、const命令
    (1)、const声明一个只读的常量。一旦声明,常量的值就不能改变,而且声明的时候必须立即初始化。

    const a = 1;
    console.log(a);
    a= 2;//TypeError: Assignment to constant variable.
    
    const b;//SyntaxError: Missing initializer in const declaration
    

    (2)、const命令的一些特性:块级作用域,不会变量提升,同一作用域内不能重复定义。

    (3)、const声明引用型变量时,变量的地址是不能改变的。

    const obj = {};
    obj.name="nie";
    obj = {};// TypeError: Assignment to constant variable.
    

    (4) ES5之中,全局变量会作为全局对象的属性,ES6中全局变量将逐步和全局对象的属性分离,let命令,const命令,class命令声明的全局变量,不属于全局对象的属性。

    var a = 123;
    console.log(window.a);//123
    
    let b =1;
    console.log(window.b);//undefined
    

    ```

    failure

    二、变量的解构赋值

    1、 数组的解构赋值:只要等号两边的模式相同,左边的变量就会被赋予对应的值

    let [a,b,c] = [1,2,3];
    console.log(a);//1
    console.log(b);//2
    console.log(c);//3
    
    let[d,[e,[f]]] = [1,[2,[3]]];
    console.log(d);//1
    console.log(e);//2
    console.log(f);//3
    
    let[g,h,...i] = [1,2,3,4];
    

    “...i”表示数组,只能放到最后。

    let[j,...k,l] = [1,2,3,4,5];// SyntaxError: Rest element must be last element in array
    

    只要某种数据结构具有Iterator接口,都可以采用数组形式的解构赋值
    2、对象的解构赋值

    var person = {
    "name":"nie",
    "gender":"male"
    }
    
    var {name} = person;
    console.log(name);
    var {name,gender} = person;
    console.log(name);
    console.log(gender);
    
    var{name:na,gender:gen} = person;
    

    {

    }

    三、数组的扩展

    1、 Array.from()
    Array.from方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括ES6新增的数据结构Set和Map)。

    var objArrLike = {
         '0':1,
         '1':2,
         '2':3,
         length:3
    } 
    

    ES5:

    var arr = Array.prototype.slice.call(objArrLike);
    

    ES6 :

    var arr = Array.from(objArrLike); //[1,2,3]
    

    Array.of : 方法用于将一组值,转换为数组
    在ES6中Array还有其他的很多扩展可以自行查找资料

    let parent = {//目标对象

    新写法

    四、函数的扩展

    1、可以为函数的参数指定默认值。
    ES5:

    function testFun(x,y){
        y = y||"iflytek";
     console.log(x,y);
    }
    testFun("Hello") //Hello iflytek
    

    ES6:

    function testFun(x,y="iflytek"){
        console.log(x,y);
    }
    testFun("Hello"); //Hello iflytek
    

    2、与解构赋值默认值结合使用

    function testFun({x,y = 5}){
        console.log(x,y);
    }
    testFun({x:1,y:2}); //1 2
    

    也可以用数组的解构赋值对函数参数进行默认值赋值

    3、指定了默认值以后函数的长度length将失真(arguments.callee.length) 有相同的效果

    (function testFun(a,b,c =2){}).length // 2
    function testFun(a,b,c){
        console.log(arguments.callee.length);
    } //3
    

    4、rest参数,形式为“...变量名”,用于获取函数的多余参数,rest参数搭配的变量是一个数组。

    function sortNums(){
        return Array.prototype.slice.call(arguments).sort();
    }
    sortNums(3,2,1);//[1,2,3]
    
    var sortNums = (...restArgs)=>{
        return restArgs.sort();
    }
    sortNums(3,2,1) //[1,2,3]
    

    注意,rest参数之后不能再有其他参数(即只能是最后一个参数),否则会报错。

    5、扩展运算符(...) 相当于rest参数的逆运算,将数组转化成逗号分隔的参数序列 注意和[].join(‘,’)的区别

    console.log(1,2,...[3,4,5],6) //1 2 3 4 5 6 
    

    扩展运算符可以展开数组,所以不需要apply方法将数组转化成函数的参数了
    ES5:

    var args = [1,2,3]
    function testFun(x,y,z){
           console.log(x,y,z);
    }
    testFun.apply(null,args); //1 2 3
    

    ES6:

    testFun(...args);//1 2 3
    

    6、扩展运算符的应用:
    (1)、合并数组
    ES5:

    [1,2].concat([3,4]);//[1,2,3,4]
    

    ES6:

    [1,2,...[3,4]] //[1,2,3,4]
    

    (2)、将字符串转化成字符串数组

    [...'123'] //['1','2','3']
    

    注:任何Iterator接口的对象,都可以用扩展运算符转为真正的数组

    7、箭头函数
    ES6允许使用“箭头”(=>)定义函数

    var f = (x)=>x;
    var f = function(x){
       return x;
    } 
    
    var add = (x,y)=>{
     return x y;
    }
    
    var add = function(x,y){
        return x y;
    }
    

    注意:
    (1)、函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
    (2)、不可以当做构造函数,不可以使用new命令,报错。
    (3)、不可以用arguments对象,该对象在函数体内不存在,用rest参数代替。
    (4)、不可以使用yield命令,因此箭头函数不能用作Generator函数。

    var id = 1;
    function test(){
        setTimeout(function(){
            console.log(this.id);
        });
    }
    test.call({id:2}) //1
    
    function test(){
        setTimeout(()=>{
            console.log(this.id);
        });
    }
    test.call({id:2}) //2
    

    name: '父',

    if (Reflect.defineProperty(target, property, attributes)) {

    五、对象的扩展

    1、 ES6允许直接写入变量和函数,作为对象的属性和方法。ES6允许对象之中只写属性名,不写属性值。此时,属性值等于属性名所代表的变量。

    var name = "iflytek";
        var person = {
        name,
        gender:'male',
        sayName(){
            console.log(name),
        }
    }
    

    2、 属性名表达式,ES6允许字面量定义对象时,用方法二(表达式)作为对象的属性名,即把表达式放在方括号内
    ES5:

    var person={
        name:"nie",
        gender:"male"
    }
    

    ES6:

    var person = {
        ["name"]:"nie",
        ["gen" "der"]:"male"
    }
    

    增强的对象字面量
    对象字面量被增强了,写法更加简洁与灵活,同时在定义对象的时候能够做的事情更多了:
    (1)可以在对象字面量里面定义原型
    (2)定义方法可以不用function关键字
    (3)直接调用父对象方法

    var person = {
        name:"nie",
        sayName(){
            console.log(this.name);
        }
    }
    
    var malePerson = {
        __proto__:person,
        gender:"male",
        sayGender(){
            console.log(this.gender);
        }
    }
    
    malePerson.name;//nie
    malePerson.sayName();//nie
    

    age: 60

    success

    六、Symbol类型

    ES6引入了一种新的原始数据类型Symbol,表示独一无二的值。它是JavaScript语言的第七种数据类型,前六种是:Undefined、Null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。

    对象的属性名现在可以有两种类型,一种是原来就有的字符串,另一种就是新增的Symbol类型。凡是属性名属于Symbol类型,就都是独一无二的,可以保证不会与其他属性名产生冲突

    var symbolStr = Symbol(); //作为对象的属性,用法和字符串一样的
    

    第一种写法:

    var a={}
    a[symbolStr] = "iflytek";
    

    第二种写法:

    var a = {
        [symbolStr]:"iflytek"
    }
    

    第三种写法:

    var a= {};
    Object.defineProperty(a,symbol,{value:"iflytek"})
    
    a[symbolStr]//iflytek
    

    };

    }else{

    七、Proxy概述

    Proxy用于修改某些操作的默认行为,等同于在语言层面做出修改。Proxy可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。

    实例:

    var target = {name:"nie"};
    
    var obj = new Proxy(target,{
        set:function(target,key,reciver){
        console.log("set "  key  "value :"  reciver);
            //target[key] = reciver;
            return Reflect.set(target,key,reciver);
        }
    });
    

    下面是Proxy支持的拦截操作一览。
    对于可以设置、但没有设置拦截的操作,则直接落在目标对象上,按照原先的方式产生结果。
    (1) get(target, propKey, receiver)
    拦截对象属性的读取,比如proxy.foo和proxy['foo']。
    最后一个参数receiver是一个对象,可选,参见下面Reflect.get的部分。
    (2) set(target, propKey, value, receiver)
    拦截对象属性的设置,比如proxy.foo = v或proxy['foo'] = v,返回一个布尔值。
    (3) has(target, propKey)
    拦截propKey in proxy的操作,以及对象的hasOwnProperty方法,返回一个布尔值。
    (4) deleteProperty(target, propKey)
    拦截delete proxy[propKey]的操作,返回一个布尔值。
    (5) ownKeys(target)
    拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy),返回一个数组。该方法返回对象所有自身的属性,而Object.keys()仅返回对象可遍历的属性。
    (6) getOwnPropertyDescriptor(target, propKey)
    拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。
    (7) defineProperty(target, propKey, propDesc)
    拦截Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一个布尔值。
    (8) preventExtensions(target)
    拦截Object.preventExtensions(proxy),返回一个布尔值。
    (9) getPrototypeOf(target)
    拦截Object.getPrototypeOf(proxy),返回一个对象。
    (10) isExtensible(target)
    拦截Object.isExtensible(proxy),返回一个布尔值。
    (11) setPrototypeOf(target, proto)
    拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。
    如果目标对象是函数,那么还有两种额外操作可以拦截。
    (12) apply(target, object, args)
    拦截Proxy实例作为函数调用的操作,比如proxy(...args)、proxy.call(object, ...args)、proxy.apply(...)。
    (13) construct(target, args)
    拦截Proxy实例作为构造函数调用的操作,比如new proxy(...args)。

    let handler = {//规则

    failure

    八、Reflect概述

    (1) 将Object对象的一些明显属于语言内部的方法(比如Object.defineProperty),放到Reflect对象上。现阶段,某些方法同时在Object和Reflect对象上部署,未来的新方法将只部署在Reflect对象上。
    (2) 修改某些Object方法的返回结果,让其变得更合理。比如,Object.defineProperty(obj, name, desc)在无法定义属性时,会抛出一个错误,而Reflect.defineProperty(obj, name, desc)则会返回false。
    (3) 让Object操作都变成函数行为。某些Object操作是命令式,比如name in obj和delete obj[name],而Reflect.has(obj, name)和Reflect.deleteProperty(obj, name)让它们变成了函数行为。

    (4) Reflect对象的方法与Proxy对象的方法一一对应,只要是Proxy对象的方法,就能在Reflect对象上找到对应的方法。这就让Proxy对象可以方便地调用对应的Reflect方法,完成默认行为,作为修改行为的基础。也就是说,不管Proxy怎么修改默认行为,你总可以在Reflect上获取默认行为。

    Reflect对象的方法清单如下,共13个。
    • Reflect.apply(target,thisArg,args)
    • Reflect.construct(target,args)
    • Reflect.get(target,name,receiver)
    • Reflect.set(target,name,value,receiver)
    • Reflect.defineProperty(target,name,desc)
    • Reflect.deleteProperty(target,name)
    • Reflect.has(target,name)
    • Reflect.ownKeys(target)
    • Reflect.isExtensible(target)
    • Reflect.preventExtensions(target)
    • Reflect.getOwnPropertyDescriptor(target, name)
    • Reflect.getPrototypeOf(target)
    • Reflect.setPrototypeOf(target, prototype)
    上面这些方法的作用,大部分与Object对象的同名方法的作用都是相同的,而且它与Proxy对象的方法是一一对应的。

    /*

    }console.log('assign' in Object === Reflect.has(Object, 'assign'))//true

    九、Set/Map 概述

    1、ES6提供了新的数据结构Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。

    3、 JavaScript的对象(Object),本质上是键值对的集合(Hash结构),但是传统上只能用字符串当作键。这给它的使用带来了很大的限制。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object结构提供了“字符串—值”的对应,Map结构提供了“值—值”的对应,是一种更完善的Hash结构实现。如果你需要“键值对”的数据结构,Map比Object更合适。

    * target 目标对象

    Proxy(target, {

    十、数据结构的默认Iterator接口

    Iterator接口的目的,就是为所有数据结构,提供了一种统一的访问机制,即for...of循环(详见下文)。当使用for...of循环遍历某种数据结构时,该循环会自动去寻找Iterator接口。
    ES6规定,默认的Iterator接口部署在数据结构的Symbol.iterator属性,或者说,一个数据结构只要具有Symbol.iterator属性,就可以认为是“可遍历的”(iterable)。调用Symbol.iterator方法,就会得到当前数据结构默认的遍历器生成函数。Symbol.iterator本身是一个表达式,返回Symbol对象的iterator属性,这是一个预定义好的、类型为Symbol的特殊值,所以要放在方括号内(请参考Symbol一章)。
    在ES6中,有三类数据结构原生具备Iterator接口:数组、某些类似数组的对象、Set和Map结构。for...of循环会自动遍历它们。除此之外,其他数据结构(主要是对象)的Iterator接口,都需要自己在Symbol.iterator属性上面部署,这样才会被for...of循环遍历。

    let obj = {
        data:['a','b'],
        [Symbol.iterator]:function(){
            const self = this;
            let index = 0;
            return {
                next(){
                    if(index<self.data.length){
                        return {
                            value:self.data[index  ],
                            done:false
                        };
                    }else{
                        return {value:undefined,done:true};
                    }
                }
            }
        }
    }
    
    for(item of obj){
        console.log(item);//a   b
    }
    

    * key 属性名

    set:function(target, name, value, receiver){

    十一、Generator函数

    1、调用Generator函数,返回一个遍历器对象,代表Generator函数的内部指针。以后,每次调用遍历器对象的next方法,就会返回一个有着value和done两个属性的对象。value属性表示当前的内部状态的值,是yield语句后面那个表达式的值;done属性是一个布尔值,表示是否遍历结束。

    2、yield语句
    由于Generator函数返回的遍历器对象,只有调用next方法才会遍历下一个内部状态,所以其实提供了一种可以暂停执行的函数。yield语句就是暂停标志。
    遍历器对象的next方法的运行逻辑如下。
    (1) 遇到yield语句,就暂停执行后面的操作,并将紧跟在yield后面的那个表达式的值,作为返回的对象的value属性值。
    (2) 下一次调用next方法时,再继续往下执行,直到遇到下一个yield语句。
    (3) 如果没有再遇到新的yield语句,就一直运行到函数结束,直到return语句为止,并将return语句后面的表达式的值,作为返回的对象的value属性值。
    (4) 如果该函数没有return语句,则返回的对象的value属性值为undefined。
    需要注意的是,yield语句后面的表达式,只有当调用next方法、内部指针指向该语句时才会执行,因此等于为JavaScript提供了手动的“惰性求值”(Lazy Evaluation)的语法功能。

    实例:

    function* funGenerator(){
        yield console.log("this is a yield function");
    }
    
    var f = funGenerator();
    
    setTimeout(()=>{
        f.next();
    },2*1000);
    

    * value 设置的属性值

    var success = Reflect.set(target, name, value, receiver)

    十二、Promise对象

    Promise是异步编程的一种解决方案。有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise对象提供统一的接口,使得控制异步操作更加容易。

    示例:

    var testPromise = function(x){
        return new Promise(function(resolve,reject){
            if(x>5){
                resolve(x);
            }else{
                reject(x);
            }
        });
    }
    
    testPromise(1).then(function(value){
        console.log(value  " 大于5");
    },function(value){
        console.log(value   " 小于5");
    });
    

    1、Promise.prototype.then
    then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法。第一个回调函数完成以后,会将返回结果作为参数,传入第二个回调函数。

    示例:

    testPromise(1).then(function(value){
        console.log(value  " 大于5");
        return value  1;
    },function(value){
        console.log(value   " 小于5");
        return value  1
    }).then(function(){
    }); //此时第二个函数是调用不到的
    

    2、Promise.prototype.catch
    Promise.prototype.catch 是 .then(null,rejection)的别名,用来指定发生错误时的回调函数。
    如果Promise的状态已经变为Resolved,再抛出错误是无效的。

    var promise = new Promise(function(resolve,reject){
    resolve("ok");
    throw new Error("err");
    });
    
    promise.then(function(value){
    console.log(value);
    }).catch(err){
    console.log("错误信息:" err.message);
    }
    

    Promise对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获

    catch方法返回的还是一个Promise对象,因此后面还可以接着调用then方法。
    catch方法指定的回调函数,会接着运行后面那个then方法指定的回调函数。如果没有报错,则会跳过catch方法。

    * receiver 重定向目标对象

    /if (success) {

    十三、Class

    1、Javascript的传统方法是通过构造函数来定义并生成新的对象,ES6提供了更接近传统语言的写法,引入了Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。
    传统写法:

    function Person(name,age){
        this.name = name;
        this.age = age;
    }
    Person.prototype.sayName = function(){
        console.log(this.name);
    }
    
    var person = new Person();
    
    ES6:
    class Person{
        constructor(name,age){
            this.name = name;
            this.age  = age;
        }
    
        sayName(){
            console.log(this.name);
        }
    }
    
    var person = new Person(); 
    
    console.log(typeof Person);//function
    console.log(Person.prototype.constructor === Person);//true
    

    2、类的数据类型就是函数,类本身就指向构造函数。
    使用的时候,也是直接对类使用new命令,跟构造函数的用法完全一致。
    事实上,实例的属性除非显式定义在其本身(即定义在this对象上),否则都是定义在原型上(即定义在class上)。可以通过打印prototype属性进行验证。
    3、class不存在变量提升

    var person = new Person();
    class Person{
    }//ReferenceError: Person is not defined
    

    * */

    新葡亰496net 7

    Class的继承

    Class之间可以通过extends关键字实现继承。

    class MalePerson extends Person{
        constructor(name,age,gender){
            super(name,age);
            this.gender = gender;
    }
    sayGender(){
        console.log(this.gender);
    }
    }
    
    MalePerson.__proto__ = 
    

    set (target, key, value, receiver) {

    // log('')

    Super关键字

    super这个关键字,有两种用法,含义不同。
    (1) 作为函数调用时(即super(...args)),super代表父类的构造函数。
    (2) 作为对象调用时(即super.prop或super.method()),super代表父类。注意,此时super即可以引用父类实例的属性和方法,也可以引用父类的静态方法。

    class Person{
        constructor(name,age){
            this.name = name;
            this.age  = age;
        }
    
        sayName(){
            console.log(this.name);
        }
    }
    
    class MalePerson extends Person{
        constructor(name,age,gender){
            super(name,age);
            this.gender = gender;
        }
        saySuperName(){
            console.log(super.name);//undefined
            console.log(this.name);//nie
            super.sayName();//nie
        }
    }
    

    super.IdentifierName作为getter使用时,表示函数B的prototype属性对象的[[prototype]];
    getter : SubClas.prototype.proto / SuperClass.prototype

    super.IdentifierName作为setter使用时,super表示this;
    setter: this

    若通过super.IdentifierName()来调用函数,则此时的函数相当于 .call(this)来调用
    IdentifierName.all(this)

    console.log('执行了修改');

    // }

    Class的静态方法

    类中定义的方法前面加上static关键字,即为静态方法,通过Class.FunctionName的形式调用。
    父类的静态方法,可以被子类继承。可以通过super关键字调用。

    class MalePerson extends Person{
        constructor(name,age,gender){
            super(name,age);
        }
    
        static sayHello(){
           alert("Hello");
           //alert(MalePerson.hello);
        }
    }
    
    MalePerson.hello  = "hello"; // ES6明确规定,Class内部只有静态方法,没有静态属性。只能在外部定义静态属性,ES7可能会做修改
    MalePerson.sayHello();
    

    Reflect.set(target, key, value);

    // }

    十四、字符串模板

    字符串模板相对简单易懂些。ES6中允许使用反引号 ` 来创建字符串,此种方法创建的字符串里面可以包含由美元符号加花括号包裹的变量${vraible}。

    var name = "nie";
    console.log(`my name is ${name}`);
    

    参考资料:
    阮一峰 ECMAScript6入门
    ES6新特性概览

    },

    // })

    get (target, key, receiver) {

    // Reflect对象的静态方法13个

    console.log('执行了取值');

    //1.

    return Reflect.get(target, key);

    //var myObject = {

    }

    // foo:1,

    };

    // bar:2,

    let parentProxy = new Proxy(parent, handler);

    // get baz(){

    parentProxy.tell = '哈哈';//执行了修改

    // return this.foo this.bar;

    let parentName = parentProxy.name;//执行了取值

    // }

    console.dir(parent)//实例parent有tell属性

    //}

    }

    //console.log(Reflect.get(myObject, 'baz'))////如果第一个参数不是对象则会报错

    ```

    ////2.

    ###Proxy支持的拦截操作

    //var obj = {

    *get(target, key, receiver):拦截对象属性的读取

    // foo:4,

    *set(target, key, value, receiver):拦截对象属性的设置

    // set bar (value){

    *has(target, key):拦截Key in proxy的操作,返回一个布尔值

    // return this.foo = value;

    *deleteProperty(target, key)

    // }

    *ownKeys()

    //}

    *getOwnPropertyDescriptor()

    //var myReceiverObject = {

    *defineProperty()

    // foo:0

    *preventExtensions()

    //}

    *getPrototypeOf()

    //Reflect.set(obj,'bar', 1, myReceiverObject);

    *isExtensible()

    //obj.foo // 4

    *setPrototypeOf()

    //myReceiverObject.foo;//1

    *apply()

    ////注意,Reflect.set会触发Proxy.defineProperty拦截

    *constructor()

    //letp = {

    ###Proxy各拦截操作实例

    // a:'a'

    ```

    //};

    {

    //let handler = {

    //get(), set()

    // det(target, key, value, receiver){

    let handler = {

    // console.log('set');

    set (target, key, value, receiver) {

    // Reflect.set(target, key, value, receiver)

    console.log('执行了set');

    // },

    //如果在此处不进行值得改变行为,则值并不能被改变

    // defineProperty(target,key, attributes){

    Reflect.set(target, key, value);

    // console.log('deleteProperty');

    },

    // Reflect.defineProperty(target, key, attributes)

    get (target, key, receiver) {

    // }

    console.log('执行了get');

    //}

    return Reflect.get(target, key)

    //let obj = new Proxy(p, handler);

    },

    //obj.a = 'A';

    has (target, key) {

    ////set

    console.log('执行了has');

    //// defineProperxy

    return 1;//会转化返回的值为boolean类型

    //// 上面代码中,Proxy.set拦截中使用了Reflect.set,导致触发Proxy.defineP//roperty拦截。

    },

    //var myObject = {

    ownKeys (target) {

    // foo:1

    console.log('执行了ownKeys');

    //}

    return Reflect.ownKeys(target);//此时不返回,则报错

    ////旧写法

    },

    //'foo' in myObject // true

    deleteProperty (target, key) {

    // Reflect.deleteProperty(obj, name)用于删除对象的属性

    console.log('执行了deleteProperty');

    // const myObj = {foo: 'bar'};

    Reflect.deleteProperty(target, key);

    // delete myObj.foo;

    },

    // console.log(myObj)

    getOwnPropertyDescriptor (target, key) {//当设置属性为不可配置时,此处报错

    //新写法

    let descriptor = Reflect.getOwnPropertyDescriptor(target, key);

    //Reflect.deleteProperty(myObj, 'foo');

    console.log('执行了getOwnPropertyDescriptor');

    //Reflect.construct(target, args)

    },

    //function Greeting(name){

    defineProperty (target, key, value) {

    // this.name = name;

    console.log('执行了defineProperty');

    //}

    },

    ////new 的写法

    preventExtensions (target) {

    //const instance = new Greeting('张三');

    console.log('执行了preventExtensions');

    //

    },

    //Reflect.deleteProperty(myObj, 'foo');

    isExtensible (target) {

    // Reflect.getPrototypeOf方法用于读取对象的__proto__属性,对应Object.getPrototypeOf(obj)。

    console.log('执行了isExtensible');

    // Reflect.getPrototypeOf(obj)

    },

    // const myObj = new FancyThing();

    getPropertyOf (target) {

    //旧写法

    console.log('执行了getPropertyOf');

    // Object.getPrototypeOf(myObj) === FancyThing.prototype;

    },

    //新写法

    setPropertyOf (target, prototype) {

    // Reflect.getPrototypeOf(myObj) === FancyThing.prototype;

    console.log('执行了setPropertyOf');

    // Reflect.setPrototypeOf(obj, newProto)

    },

    // Reflect.setPrototypeOf方法用于设置对象的__proto__属性,返回第一个参数对象,对应Object.setPrototypeOf(obj, newProto)。

    apply (target, obj, arguments) {

    //const myObj = new FancyThing();

    console.log('执行了apply');

    // 旧写法

    if (!!obj) {

    //Object.setPrototypeOf(myObj, OtherThing.prototype);

    target.call(obj, arguments);

    // 新写法

    } else {

    //Reflect.setPrototypeOf(myObj, OtherThing.prototype);

    target(arguments);

    // 如果第一个参数不是对象,Object.setPrototypeOf会返回第一个参数本身,而Reflect.setPrototypeOf会报错。

    }

    //如果第一个参数不是对象

    },

    //Reflect.getPrototypeOf(1);// 报错

    construct (target, arguments) {

    //Object.getPrototypeOf(1); //1

    console.log('执行了construct');

    //Reflect.setPrototypeOf(1, {})//TypeError: Reflect.setPrototypeOf called on non-object

    return new target;

    //Object.setPrototypeOf(1, {}) //1

    }

    //如果第一个参数是undefind或者是null  两者都会报错

    };

    //Object.setPrototypeOf(null, {}) //// TypeError: Object.setPrototypeOf called on null or undefined

    let parent = Object.create(Object.prototype, {

    //Reflect.setPrototypeOf(null,{})//// TypeError: Reflect.setPrototypeOf called on non-object

    name: {

    //Reflect.apply(func, thisArg, args)

    value: '张三',

    // Reflect.apply方法等同于 Function.prototype.apply.call(func, thisArg, args),采用Reflect对象可以简化这种操作。

    configurable: true,//可配置:可删除,可获取, 可操作

    //const ages = [11,33, 12, 54, 18, 96];

    enumerable: true,

    //var obj = {}

    writable: false

    ////旧写法

    },

    //const youngest = Math.min.apply(obj, ages);

    age: {

    //const oldest = Math.max.apply(obj, ages);

    value: 20,

    //const type = Object.prototype.toString.call(youngest);

    configurable: true,

    //console.log(type)

    enumerable: true,

    ////新写法

    writable: true

    //const youngest = Reflect.apply(Math.min, Math, ages);

    },

    //const oldest = Reflect.apply(Math.max, Math, ages);

    gender: {

    //const type = Reflect.apply(Object.prototype.toString, youngest, [])

    value: '未知',

    // Reflect.defineProperty  用来定义对象的属性 Reflect.defineProperty(target, propertyKey, attributes)

    configurable: true,

    // function myDate(){

    enumerable: false,

    // }

    writable: false

    //旧写法

    },

    // Object.defineProperty(myDate, 'now', {

    canSleep: {

    // value:()=>Date.now()

    value: true,

    // })

    configurable: true,

    //新写法17316382398

    enumerable: true,

    // Reflect.defineProperty(myDate, 'now', {

    writable: true

    // value:()=>Date.now()

    }

    // })

    });

    // console.log(myDate.now())

    let parentProxy = new Proxy(parent, handler);

    //注意如果第一个参数不是对象,就会抛出错误,比如

    console.dir(parentProxy);

    // Reflect.defineProperty(1, 'foo')

    let name = parentProxy.name;//执行了get

    // Reflect.defineProperty(target, propertyKey, attributes)基本等同于Object.getOwnPropertyDescriptor,用于得到指定属性的描述对象,将来会替代掉后者

    console.log(name);//张三

    // var myObject = {};

    parentProxy.canSleep = false;//执行了set

    // Object.defineProperty(myObject, 'hidden', {

    console.log(parentProxy.canSleep);//false

    // value: true,

    console.log(Reflect.has(parentProxy, 'name'));//执行了has

    // enumerable: true

    //'name' in parentProxy;//执行了has

    // })

    Object.getOwnPropertyNames(parentProxy);//执行了ownKeys

    //旧写法

    //Reflect.ownKeys(parentProxy);//执行了ownKeys

    // var theDescriptor = Object.getOwnPropertyDescriptor(myObject, 'hidden')

    //Object.getOwnPropertySymbols(parentProxy);//执行了ownKeys

    //新写法

    //Object.values(parentProxy);//执行了ownKeys,并执行了2次getOwnPropertyDescriptor

    // var theDescriptor = Reflect.getOwnPropertyDescriptor('myObject', 'hidden')

    //Object.keys(parentProxy);

    // Reflect.getOwnPropertyDescriptor和Object.getOwnPropertyDescriptor的一个区别是,如果第一个参数不是对象,Object.getOwnPropertyDescriptor(1, 'foo')不报错,返回undefined,而Reflect.getOwnPropertyDescriptor(1, 'foo')会抛出错误,表示参数非法。

    //Object.entries(parentProxy);

    // Reflect.isExtensible(target) 方法对应Object.isExtensible,返回一个布尔值,表示当前对象是否可拓展

    Reflect.deleteProperty(parentProxy, 'canSleep');//执行了deleteProperty

    // const myObj = {};

    console.dir(parentProxy);

    //旧写法

    //Reflect.defineProperty();

    // Object.isExtensible(myObj)//true

    let toDownload = function () {

    //新写法

    console.log('toDownload');

    // Reflect.isExtensible(myObj)//true

    };

    // 而:

    let downloadProxy = new Proxy(toDownload, handler);

    // 如果参数不是对象,Object.isExtensible会返回false,因为非对象本来就是不可扩展的,而Reflect.isExtensible会报错。

    downloadProxy();//执行了apply

    // Object.isExtensible(1) // false

    let littleDownloadProxy = new downloadProxy();//执行了construct

    // Reflect.isExtensible(1) // 报错

    console.dir(littleDownloadProxy instanceof toDownload);

    // Reflect.preventExtensions对应Object.preventExtensions方法,用于让一个对象变为不可扩展。它返回一个布尔值,表示是否操作成功。

    }

    var obj = {};

    ```

    //旧写法

    ###Proxy.revocable()

    Object.preventExtensions(obj);  //Object {}

    > 返回一个可以取消的Proxy实例

    //新写法

    ```

    Reflect.preventExtensions(myObject)  //true

    {

    //如果参数不是对象,Object.preventExtensions在 ES5 环境报错,在 ES6 环境返回传入的参数,而Reflect.preventExtensions会报错。

    let obj = {}, handler = {};

    // ES5 环境

    let {proxy, revoke} = Proxy.revocable(obj, handler);

    Object.preventExtensions(1) // 报错

    proxy.name = 'zhangsan';

    // ES6 环境

    console.log(obj.name);//zhangsan

    Object.preventExtensions(1) // 1

    revoke();

    // 新写法

    proxy.name = 'lisi';//此时报错

    Reflect.preventExtensions(1) // 报错

    }

    // Reflect.ownkeys(target)方法用于返回对象的所有属性,基本等同于Object.getOwnPropertyNames与Object.getOwnPropertySymbols之和。

    ```

    var myObject = {

    ###this问题

    foo:1,

    > Proxy代理时,拦截目标对象内部的this会指向Proxy代理

    bar:2,

    >> 代理在调用目标对象的方法时,和通常情况下this的指向一样,即指向调用其的对象,所以此时会指向Proxy代理,有时会造成获取不到值或者出错问题,如

    [Symbol.for('baz')]:3,

    ```

    [Symbol.for('bing')]:4

    {

    };

    let map = new WeakMap();

    //旧写法

    class Parent {

    Object.getOwnPropertyNames(myObject); //['foo', 'baz']

    constructor (name) {

    Object.getOwnPropertySymbols(myObject);// //[Symbol.for('baz'), Symbol.for('bing')]

    map.set(this, name);

    //新写法

    }

    Reflect.ownKeys(myObject);// ['foo', 'bar', Symbol.for('baz'), Symbol.for('bing')]

    get name () {

    //3.实例:使用Proxy实现观察者模式

    return map.get(this, 'name');

    // 观察者模式(Observer mode)指的是函数自动观察数据对象,一旦对象有变化,函数就会自动执行。

    }

    const person = observable({

    }

    name:'张三',

    let parent =  new Parent('张三');

    age:20

    console.log(parent.name);//张三

    })

    let handler = {};

    function print(){

    let { proxy, revoke } = Proxy.revocable(parent, handler);

    console.log(`${person.name}, ${person.age}`)

    console.log(proxy.name);//undefined

    }

    }

    observe(print);

    ```

    person.name = '李四'

    > 此时也可以处理为

    //输出

    ```

    //李四, 20

    {

    // 上面代码中,数据对象person是观察目标,函数print是观察者。一旦数据对象发生变化,print就会自动执行。

    let map = new WeakMap();

    // 下面,使用 Proxy 写一个观察者模式的最简单实现,即实现observable和observe这两个函数。思路是observable函数返回一个原始对象的 Proxy 代理,拦截赋值操作,触发充当观察者的各个函数。

    class Parent {

    const queueObserves = new set();

    constructor (name) {

    const observe = fn =>queueObserves.add(fn);

    map.set(this, name);

    const observable = obj=>new Proxy(obj, {set});

    }

    function set(target, key, value, receiver){

    get name () {

    const result = Reflect.set(target, key, value, receiver);

    return map.get(this, 'name');

    queuedObservers.forEach(obsserver=> obsserver());

    }

    return result;

    }

    }

    let parent =  new Parent('张三');

    // 上面代码中,先定义了一个Set集合,所有观察者函数都放进这个集合。然后,observable函数返回原始对象的代理,拦截赋值操作。拦截函数set之中,会自动执行所有观察者。

    console.log(parent.name);//张三

    let handler = {

    get (target, key) {

    return Reflect.get(target, key);

    }

    };

    let { proxy, revoke } = Proxy.revocable(parent, handler);

    console.log(proxy.name);//张三

    }

    ```

    #####以上参考[阮一峰es6-proxy]()

    ###关于Object.create()

    > 使用指定的原型对象和属性对象创建一个新的对象

    ```

    {

    let o;

    // 创建一个原型为null的空对象

    o = Object.create(null);

    o = {};

    // 以字面量方式创建的空对象就相当于:

    o = Object.create(Object.prototype);

    o = Object.create(Object.prototype, {

    // foo会成为所创建对象的数据属性

    foo: {

    writable:true,

    configurable:true,

    value: "hello"

    },

    // bar会成为所创建对象的访问器属性

    bar: {

    configurable: false,

    get: function() { return 10 },

    set: function(value) {

    console.log("Setting `o.bar` to", value);

    }

    }

    });

    // 创建一个以另一个空对象为原型,且拥有一个属性p的对象

    o = Object.create({}, { p: { value: 42 } });

    // 省略了的属性特性默认为false,所以属性p是不可写,不可枚举,不可配置的:

    o.p = 24;

    console.log(o.p);//42

    o.q = 12;

    for (let pro in o) {

    console.log(pro);

    }//"q"

    delete o.p;//false

    //创建一个可写的,可枚举的,可配置的属性p

    o2 = Object.create({}, {

    p: {

    value: 42,

    writable: true,

    enumerable: true,

    configurable: true//可配置,可删除

    }

    });

    Reflect.set(o2, 'p', 12);

    Reflect.deleteProperty(o2, 'p');//true

    console.log(o2);

    }

    ```

    > 此段引自:[)

    本文由新葡亰496net发布于新葡亰官网,转载请注明出处:新葡亰496net增加生产数量知识点总括,观念的落

    关键词: