<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                ## 屬性的簡潔表示法 ES6允許直接寫入變量和函數,作為對象的屬性和方法。這樣的書寫更加簡潔。 ~~~ function f( x, y ) { return { x, y }; } // 等同于 function f( x, y ) { return { x: x, y: y }; } ~~~ 上面是屬性簡寫的例子,方法也可以簡寫。 ~~~ var o = { method() { return "Hello!"; } }; // 等同于 var o = { method: function() { return "Hello!"; } }; ~~~ 下面是一個更實際的例子。 ~~~ var Person = { name: '張三', //等同于birth: birth birth, // 等同于hello: function ()... hello() { console.log('我的名字是', this.name); } }; ~~~ 這種寫法用于函數的返回值,將會非常方便。 ~~~ function getPoint() { var x = 1; var y = 10; return {x, y}; } getPoint() // {x:1, y:10} ~~~ ## 屬性名表達式 JavaScript語言定義對象的屬性,有兩種方法。 ~~~ // 方法一 obj.foo = true; // 方法二 obj['a'+'bc'] = 123; ~~~ 上面代碼的方法一是直接用標識符作為屬性名,方法二是用表達式作為屬性名,這時要將表達式放在方括號之內。 但是,如果使用字面量方式定義對象(使用大括號),在ES5中只能使用方法一(標識符)定義屬性。 ~~~ var obj = { foo: true, abc: 123 }; ~~~ ES6允許字面量定義對象時,用方法二(表達式)作為對象的屬性名,即把表達式放在方括號內。 ~~~ let propKey = 'foo'; let obj = { [propKey]: true, ['a'+'bc']: 123 }; ~~~ 下面是另一個例子。 ~~~ var lastWord = "last word"; var a = { "first word": "hello", [lastWord]: "world" }; a["first word"] // "hello" a[lastWord] // "world" a["last word"] // "world" ~~~ 表達式還可以用于定義方法名。 ~~~ let obj = { ['h'+'ello']() { return 'hi'; } }; console.log(obj.hello()); // hi ~~~ ## 方法的name屬性 函數的name屬性,返回函數名。ES6為對象方法也添加了name屬性。 ~~~ var person = { sayName: function() { console.log(this.name); }, get firstName() { return "Nicholas" } } person.sayName.name // "sayName" person.firstName.name // "get firstName" ~~~ 上面代碼中,方法的name屬性返回函數名(即方法名)。如果使用了存值函數,則會在方法名前加上get。如果是存值函數,方法名的前面會加上set。 ~~~ var doSomething = function() { // ... }; console.log(doSomething.bind().name); // "bound doSomething" console.log((new Function()).name); // "anonymous" ~~~ 有兩種特殊情況:bind方法創造的函數,name屬性返回“bound”加上原函數的名字;Function構造函數創造的函數,name屬性返回“anonymous”。 ~~~ (new Function()).name // "anonymous" var doSomething = function() { // ... }; doSomething.bind().name // "bound doSomething" ~~~ ## Object.is() Object.is()用來比較兩個值是否嚴格相等。它與嚴格比較運算符(===)的行為基本一致,不同之處只有兩個:一是+0不等于-0,二是NaN等于自身。 ~~~ +0 === -0 //true NaN === NaN // false Object.is(+0, -0) // false Object.is(NaN, NaN) // true ~~~ ES5可以通過下面的代碼,部署Object.is()。 ~~~ Object.defineProperty(Object, 'is', { value: function(x, y) { if (x === y) { // 針對+0 不等于 -0的情況 return x !== 0 || 1 / x === 1 / y; } // 針對NaN的情況 return x !== x && y !== y; }, configurable: true, enumerable: false, writable: true }); ~~~ ## Object.assign() Object.assign方法用來將源對象(source)的所有可枚舉屬性,復制到目標對象(target)。它至少需要兩個對象作為參數,第一個參數是目標對象,后面的參數都是源對象。只要有一個參數不是對象,就會拋出TypeError錯誤。 ~~~ var target = { a: 1 }; var source1 = { b: 2 }; var source2 = { c: 3 }; Object.assign(target, source1, source2); target // {a:1, b:2, c:3} ~~~ 注意,如果目標對象與源對象有同名屬性,或多個源對象有同名屬性,則后面的屬性會覆蓋前面的屬性。 ~~~ var target = { a: 1, b: 1 }; var source1 = { b: 2, c: 2 }; var source2 = { c: 3 }; Object.assign(target, source1, source2); target // {a:1, b:2, c:3} ~~~ assign方法有很多用處。 **(1)為對象添加屬性** ~~~ class Point { constructor(x, y) { Object.assign(this, {x, y}); } } ~~~ 上面方法通過assign方法,將x屬性和y屬性添加到Point類的對象實例。 **(2)為對象添加方法** ~~~ Object.assign(SomeClass.prototype, { someMethod(arg1, arg2) { ··· }, anotherMethod() { ··· } }); // 等同于下面的寫法 SomeClass.prototype.someMethod = function (arg1, arg2) { ··· }; SomeClass.prototype.anotherMethod = function () { ··· }; ~~~ 上面代碼使用了對象屬性的簡潔表示法,直接將兩個函數放在大括號中,再使用assign方法添加到SomeClass.prototype之中。 **(3)克隆對象** ~~~ function clone(origin) { return Object.assign({}, origin); } ~~~ 上面代碼將原始對象拷貝到一個空對象,就得到了原始對象的克隆。 不過,采用這種方法克隆,只能克隆原始對象自身的值,不能克隆它繼承的值。如果想要保持繼承鏈,可以采用下面的代碼。 ~~~ function clone(origin) { let originProto = Object.getPrototypeOf(origin); return Object.assign(Object.create(originProto), origin); } ~~~ **(4)合并多個對象** 將多個對象合并到某個對象。 ~~~ const merge = (target, ...sources) => Object.assign(target, ...sources); ~~~ 如果希望合并后返回一個新對象,可以改寫上面函數,對一個空對象合并。 ~~~ const merge = (...sources) => Object.assign({}, ...sources); ~~~ **(5)為屬性指定默認值** ~~~ const DEFAULTS = { logLevel: 0, outputFormat: 'html' }; function processContent(options) { let options = Object.assign({}, DEFAULTS, options); } ~~~ 上面代碼中,DEFAULTS對象是默認值,options對象是用戶提供的參數。assign方法將DEFAULTS和options合并成一個新對象,如果兩者有同名屬性,則option的屬性值會覆蓋DEFAULTS的屬性值。 ## **proto**屬性,Object.setPrototypeOf(),Object.getPrototypeOf() **(1)**proto**屬性** **proto**屬性,用來讀取或設置當前對象的prototype對象。該屬性一度被正式寫入ES6草案,但后來又被移除。目前,所有瀏覽器(包括IE11)都部署了這個屬性。 ~~~ // es6的寫法 var obj = { __proto__: someOtherObj, method: function() { ... } } // es5的寫法 var obj = Object.create(someOtherObj); obj.method = function() { ... } ~~~ 有了這個屬性,實際上已經不需要通過Object.create()來生成新對象了。 **(2)Object.setPrototypeOf()** Object.setPrototypeOf方法的作用與**proto**相同,用來設置一個對象的prototype對象。它是ES6正式推薦的設置原型對象的方法。 ~~~ // 格式 Object.setPrototypeOf(object, prototype) // 用法 var o = Object.setPrototypeOf({}, null); ~~~ 該方法等同于下面的函數。 ~~~ function (obj, proto) { obj.__proto__ = proto; return obj; } ~~~ **(3)Object.getPrototypeOf()** 該方法與setPrototypeOf方法配套,用于讀取一個對象的prototype對象。 ~~~ Object.getPrototypeOf(obj) ~~~ ## Symbol ### 概述 在ES5中,對象的屬性名都是字符串,這容易造成屬性名的沖突。比如,你使用了一個他人提供的對象,但又想為這個對象添加新的方法,新方法的名字有可能與現有方法產生沖突。如果有一種機制,保證每個屬性的名字都是獨一無二的就好了,這樣就從根本上防止屬性名的沖突。這就是ES6引入Symbol的原因。 ES6引入了一種新的原始數據類型Symbol,表示獨一無二的ID。它通過Symbol函數生成。這就是說,對象的屬性名現在可以有兩種類型,一種是原來就有的字符串,另一種就是新增的Symbol類型。凡是屬性名屬于Symbol類型,就都是獨一無二的,可以保證不會與其他屬性名產生沖突。 ~~~ let s = Symbol(); typeof s // "symbol" ~~~ 上面代碼中,變量s就是一個獨一無二的ID。typeof運算符的結果,表明變量s是Symbol數據類型,而不是字符串之類的其他類型。 注意,Symbol函數前不能使用new命令,否則會報錯。這是因為生成的Symbol是一個原始類型的值,不是對象。也就是說,由于Symbol值不是對象,所以不能添加屬性。基本上,它是一種類似于字符串的數據類型。 Symbol函數可以接受一個字符串作為參數,表示對Symbol實例的描述,主要是為了在控制臺顯示,或者轉為字符串時,比較容易區分。 ~~~ var s1 = Symbol('foo'); var s2 = Symbol('bar'); s1 // Symbol(foo) s2 // Symbol(bar) s1.toString() // "Symbol(foo)" s2.toString() // "Symbol(bar)" ~~~ 上面代碼中,s1和s2是兩個Symbol值。如果不加參數,它們在控制臺的輸出都是`Symbol()`,不利于區分。有了參數以后,就等于為它們加上了描述,輸出的時候就能夠分清,到底是哪一個值。 注意,Symbol函數的參數只是表示對當前Symbol類型的值的描述,因此相同參數的Symbol函數的返回值是不相等的。 ~~~ // 沒有參數的情況 var s1 = Symbol(); var s2 = Symbol(); s1 === s2 // false // 有參數的情況 var s1 = Symbol("foo"); var s2 = Symbol("foo"); s1 === s2 // false ~~~ 上面代碼中,s1和s2都是Symbol函數的返回值,而且參數相同,但是它們是不相等的。 Symbol類型的值不能與其他類型的值進行運算,會報錯。 ~~~ var sym = Symbol('My symbol'); "your symbol is " + sym // TypeError: can't convert symbol to string `your symbol is ${sym}` // TypeError: can't convert symbol to string ~~~ 但是,Symbol類型的值可以轉為字符串。 ~~~ var sym = Symbol('My symbol'); String(sym) // 'Symbol(My symbol)' sym.toString() // 'Symbol(My symbol)' ~~~ ### 作為屬性名的Symbol 由于每一個Symbol值都是不相等的,這意味著Symbol值可以作為標識符,用于對象的屬性名,就能保證不會出現同名的屬性。這對于一個對象由多個模塊構成的情況非常有用,能防止某一個鍵被不小心改寫或覆蓋。 ~~~ var mySymbol = Symbol(); // 第一種寫法 var a = {}; a[mySymbol] = 'Hello!'; // 第二種寫法 var a = { [mySymbol]: 123 }; // 第三種寫法 var a = {}; Object.defineProperty(a, mySymbol, { value: 'Hello!' }); // 以上寫法都得到同樣結果 a[mySymbol] // "Hello!" ~~~ 上面代碼通過方括號結構和Object.defineProperty,將對象的屬性名指定為一個Symbol值。 注意,Symbol值作為對象屬性名時,不能用點運算符。 ~~~ var mySymbol = Symbol(); var a = {}; a.mySymbol = 'Hello!'; a[mySymbol] // undefined a['mySymbol'] // "Hello!" ~~~ 上面代碼中,因為點運算符后面總是字符串,所以不會讀取mySymbol作為標識名所指代的那個值,導致a的屬性名實際上是一個字符串,而不是一個Symbol值。 同理,在對象的內部,使用Symbol值定義屬性時,Symbol值必須放在方括號之中。 ~~~ let s = Symbol(); let obj = { [s]: function (arg) { ... } }; obj[s](123); ~~~ 上面代碼中,如果s不放在方括號中,該屬性的鍵名就是字符串s,而不是s所代表的那個Symbol值。 采用增強的對象寫法,上面代碼的obj對象可以寫得更簡潔一些。 ~~~ let obj = { [s](arg) { ... } }; ~~~ Symbol類型還可以用于定義一組常量,保證這組常量的值都是不相等的。 ~~~ log.levels = { DEBUG: Symbol('debug'), INFO: Symbol('info'), WARN: Symbol('warn'), }; log(log.levels.DEBUG, 'debug message'); log(log.levels.INFO, 'info message'); ~~~ 還有一點需要注意,Symbol值作為屬性名時,該屬性還是公開屬性,不是私有屬性。 ### 屬性名的遍歷 Symbol作為屬性名,該屬性不會出現在for...in、for...of循環中,也不會被`Object.keys()`、`Object.getOwnPropertyNames()`返回。但是,它也不是私有屬性,有一個Object.getOwnPropertySymbols方法,可以獲取指定對象的所有Symbol屬性名。 Object.getOwnPropertySymbols方法返回一個數組,成員是當前對象的所有用作屬性名的Symbol值。 ~~~ var obj = {}; var a = Symbol('a'); var b = Symbol.for('b'); obj[a] = 'Hello'; obj[b] = 'World'; var objectSymbols = Object.getOwnPropertySymbols(obj); objectSymbols // [Symbol(a), Symbol(b)] ~~~ 下面是另一個例子,Object.getOwnPropertySymbols方法與for...in循環、Object.getOwnPropertyNames方法進行對比的例子。 ~~~ var obj = {}; var foo = Symbol("foo"); Object.defineProperty(obj, foo, { value: "foobar", }); for (var i in obj) { console.log(i); // 無輸出 } Object.getOwnPropertyNames(obj) // [] Object.getOwnPropertySymbols(obj) // [Symbol(foo)] ~~~ 上面代碼中,使用Object.getOwnPropertyNames方法得不到Symbol屬性名,需要使用Object.getOwnPropertySymbols方法。 另一個新的API,Reflect.ownKeys方法可以返回所有類型的鍵名,包括常規鍵名和Symbol鍵名。 ~~~ let obj = { [Symbol('my_key')]: 1, enum: 2, nonEnum: 3 }; Reflect.ownKeys(obj) // [Symbol(my_key), 'enum', 'nonEnum'] ~~~ 由于以Symbol值作為名稱的屬性,不會被常規方法遍歷得到。我們可以利用這個特性,為對象定義一些非私有的、但又希望只用于內部的方法。 ~~~ var size = Symbol('size'); class Collection { constructor() { this[size] = 0; } add(item) { this[this[size]] = item; this[size]++; } static sizeOf(instance) { return instance[size]; } } var x = new Collection(); Collection.sizeOf(x) // 0 x.add('foo'); Collection.sizeOf(x) // 1 Object.keys(x) // ['0'] Object.getOwnPropertyNames(x) // ['0'] Object.getOwnPropertySymbols(x) // [Symbol(size)] ~~~ 上面代碼中,對象x的size屬性是一個Symbol值,所以`Object.keys(x)`、`Object.getOwnPropertyNames(x)`都無法獲取它。這就造成了一種非私有的內部方法的效果。 ### Symbol.for(),Symbol.keyFor() 有時,我們希望重新使用同一個Symbol值,`Symbol.for`方法可以做到這一點。它接受一個字符串作為參數,然后搜索有沒有以該參數作為名稱的Symbol值。如果有,就返回這個Symbol值,否則就新建并返回一個以該字符串為名稱的Symbol值。 ~~~ var s1 = Symbol.for('foo'); var s2 = Symbol.for('foo'); s1 === s2 // true ~~~ 上面代碼中,s1和s2都是Symbol值,但是它們都是同樣參數的`Symbol.for`方法生成的,所以實際上是同一個值。 `Symbol.for()`與`Symbol()`這兩種寫法,都會生成新的Symbol。它們的區別是,前者會被登記在全局環境中供搜索,后者不會。`Symbol.for()`不會每次調用就返回一個新的Symbol類型的值,而是會先檢查給定的key是否已經存在,如果不存在才會新建一個值。比如,如果你調用`Symbol.for("cat")`30次,每次都會返回同一個Symbol值,但是調用`Symbol("cat")`30次,會返回30個不同的Symbol值。 ~~~ Symbol.for("bar") === Symbol.for("bar") // true Symbol("bar") === Symbol("bar") // false ~~~ 上面代碼中,由于`Symbol()`寫法沒有登記機制,所以每次調用都會返回一個不同的值。 Symbol.keyFor方法返回一個已登記的Symbol類型值的key。 ~~~ var s1 = Symbol.for("foo"); Symbol.keyFor(s1) // "foo" var s2 = Symbol("foo"); Symbol.keyFor(s2) // undefined ~~~ 上面代碼中,變量s2屬于未登記的Symbol值,所以返回undefined。 需要注意的是,`Symbol.for`為Symbol值登記的名字,是全局環境的,可以在不同的iframe或service worker中取到同一個值。 ~~~ iframe = document.createElement('iframe'); iframe.src = String(window.location); document.body.appendChild(iframe); iframe.contentWindow.Symbol.for('foo') === Symbol.for('foo') // true ~~~ 上面代碼中,iframe窗口生成的Symbol值,可以在主頁面得到。 ### 內置的Symbol值 除了定義自己使用的Symbol值以外,ES6還提供一些內置的Symbol值,指向語言內部使用的方法。 **(1)Symbol.hasInstance** 對象的Symbol.hasInstance屬性,指向一個內部方法。該對象使用instanceof運算符時,會調用這個方法,判斷該對象是否為某個構造函數的實例。比如,`foo instanceof Foo`在語言內部,實際調用的是`Foo[Symbol.hasInstance](foo)`。 **(2)Symbol.isConcatSpreadable** 對象的Symbol.isConcatSpreadable屬性,指向一個方法。該對象使用Array.prototype.concat()時,會調用這個方法,返回一個布爾值,表示該對象是否可以擴展成數組。 **(3)Symbol.isRegExp** 對象的Symbol.isRegExp屬性,指向一個方法。該對象被用作正則表達式時,會調用這個方法,返回一個布爾值,表示該對象是否為一個正則對象。 **(4)Symbol.match** 對象的Symbol.match屬性,指向一個函數。當執行`str.match(myObject)`時,如果該屬性存在,會調用它,返回該方法的返回值。 **(5)Symbol.iterator** 對象的Symbol.iterator屬性,指向該對象的默認遍歷器方法,即該對象進行for...of循環時,會調用這個方法,返回該對象的默認遍歷器,詳細介紹參見《Iterator和for...of循環》一章。 ~~~ class Collection { *[Symbol.iterator]() { let i = 0; while(this[i] !== undefined) { yield this[i]; ++i; } } } let myCollection = new Collection(); myCollection[0] = 1; myCollection[1] = 2; for(let value of myCollection) { console.log(value); } // 1 // 2 ~~~ **(6)Symbol.toPrimitive** 對象的Symbol.toPrimitive屬性,指向一個方法。該對象被轉為原始類型的值時,會調用這個方法,返回該對象對應的原始類型值。 **(7)Symbol.toStringTag** 對象的Symbol.toStringTag屬性,指向一個方法。在該對象上面調用`Object.prototype.toString`方法時,如果這個屬性存在,它的返回值會出現在toString方法返回的字符串之中,表示對象的類型。也就是說,這個屬性可以用來定制`[object Object]`或`[object Array]`中object后面的那個字符串。 ~~~ class Collection { get [Symbol.toStringTag]() { return 'xxx'; } } var x = new Collection(); Object.prototype.toString.call(x) // "[object xxx]" ~~~ **(8)Symbol.unscopables** 對象的Symbol.unscopables屬性,指向一個對象。該對象指定了使用with關鍵字時,那些屬性會被with環境排除。 ~~~ Array.prototype[Symbol.unscopables] // { // copyWithin: true, // entries: true, // fill: true, // find: true, // findIndex: true, // keys: true // } Object.keys(Array.prototype[Symbol.unscopables]) // ['copyWithin', 'entries', 'fill', 'find', 'findIndex', 'keys'] ~~~ 上面代碼說明,數組有6個屬性,會被with命令排除。 ~~~ // 沒有unscopables時 class MyClass { foo() { return 1; } } var foo = function () { return 2; }; with (MyClass.prototype) { foo(); // 1 } // 有unscopables時 class MyClass { foo() { return 1; } get [Symbol.unscopables]() { return { foo: true }; } } var foo = function () { return 2; }; with (MyClass.prototype) { foo(); // 2 } ~~~ ## Proxy ### 概述 Proxy用于修改某些操作的默認行為,等同于在語言層面做出修改,所以屬于一種“元編程”(meta programming),即對編程語言進行編程。 Proxy可以理解成,在目標對象之前架設一層“攔截”,外界對該對象的訪問,都必須先通過這層攔截,因此提供了一種機制,可以對外界的訪問進行過濾和改寫。Proxy這個詞的原意是代理,用在這里表示由它來“代理”某些操作,可以譯為“代理器”。 ~~~ var obj = new Proxy({}, { get: function (target, key, receiver) { console.log(`getting ${key}!`); return Reflect.get(target, key, receiver); }, set: function (target, key, value, receiver) { console.log(`setting ${key}!`); return Reflect.set(target, key, value, receiver); } }); ~~~ 上面代碼對一個空對象架設了一層攔截,重定義了屬性的讀取(get)和設置(set)行為。這里暫時不解釋具體的語法,只看運行結果。對設置了攔截行為的對象obj,去讀寫它的屬性,就會得到下面的結果。 ~~~ obj.count = 1 // setting count! ++obj.count // getting count! // setting count! // 2 ~~~ 上面代碼說明,Proxy實際上重載(overload)了點運算符,即用自己的定義覆蓋了語言的原始定義。 ES6原生提供Proxy構造函數,用來生成Proxy實例。 ~~~ var proxy = new Proxy(target, handler) ~~~ Proxy對象的所用用法,都是上面這種形式,不同的只是handler參數的寫法。其中,`new Proxy()`表示生成一個Proxy實例,target參數表示所要攔截的目標對象,handler參數也是一個對象,用來定制攔截行為。 下面是另一個攔截讀取屬性行為的例子。 ~~~ var proxy = new Proxy({}, { get: function(target, property) { return 35; } }); proxy.time // 35 proxy.name // 35 proxy.title // 35 ~~~ 上面代碼中,作為構造函數,Proxy接受兩個參數。第一個參數是所要代理的目標對象(上例是一個空對象),即如果沒有Proxy的介入,操作原來要訪問的就是這個對象;第二個參數是一個配置對象,對于每一個被代理的操作,需要提供一個對應的處理函數,該函數將攔截對應的操作。比如,上面代碼中,配置對象有一個get方法,用來攔截對目標對象屬性的訪問請求。get方法的兩個參數分別是目標對象和所要訪問的屬性。可以看到,由于攔截函數總是返回35,所以訪問任何屬性都得到35。 注意,要使得Proxy起作用,必須針對Proxy實例(上例是proxy對象)進行操作,而不是針對目標對象(上例是空對象)進行操作。 一個技巧是將Proxy對象,設置到`object.proxy`屬性,從而可以在object對象上調用。 ~~~ var object = { proxy: new Proxy(target, handler) } ~~~ Proxy實例也可以作為其他對象的原型對象。 ~~~ var proxy = new Proxy({}, { get: function(target, property) { return 35; } }); let obj = Object.create(proxy); obj.time // 35 ~~~ 上面代碼中,proxy對象是obj對象的原型,obj對象本身并沒有time屬性,所有根據原型鏈,會在proxy對象上讀取該屬性,導致被攔截。 同一個攔截器函數,可以設置攔截多個操作。 ~~~ 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 args[1]; } }; var fproxy = new Proxy(function(x,y) { return x+y; }, handler); fproxy(1,2); // 1 new fproxy(1,2); // 2 fproxy.prototype; // Object.prototype fproxy.foo; // 'Hello, foo' ~~~ 下面是Proxy支持的攔截操作一覽。 對于可以設置、但沒有設置攔截的操作,則直接落在目標對象上,按照原先的方式產生結果。 **(1)get(target, propKey, receiver)** 攔截對象屬性的讀取,比如`proxy.foo`和`proxy['foo']`,返回類型不限。最后一個參數receiver可選,當target對象設置了propKey屬性的get函數時,receiver對象會綁定get函數的this對象。 **(2)set(target, propKey, value, receiver)** 攔截對象屬性的設置,比如`proxy.foo = v`或`proxy['foo'] = v`,返回一個布爾值。 **(3)has(target, propKey)** 攔截`propKey in proxy`的操作,返回一個布爾值。 **(4)deleteProperty(target, propKey)** 攔截`delete proxy[propKey]`的操作,返回一個布爾值。 **(5)enumerate(target)** 攔截`for (var x in proxy)`,返回一個遍歷器。 **6)hasOwn(target, propKey)** 攔截`proxy.hasOwnProperty('foo')`,返回一個布爾值。 **(7)ownKeys(target)** 攔截`Object.getOwnPropertyNames(proxy)`、`Object.getOwnPropertySymbols(proxy)`、`Object.keys(proxy)`,返回一個數組。該方法返回對象所有自身的屬性,而`Object.keys()`僅返回對象可遍歷的屬性。 **(8)getOwnPropertyDescriptor(target, propKey)** 攔截`Object.getOwnPropertyDescriptor(proxy, propKey)`,返回屬性的描述對象。 **(9)defineProperty(target, propKey, propDesc)** 攔截`Object.defineProperty(proxy, propKey, propDesc)`、`Object.defineProperties(proxy, propDescs)`,返回一個布爾值。 **(10)preventExtensions(target)** 攔截`Object.preventExtensions(proxy)`,返回一個布爾值。 **(11)getPrototypeOf(target)** 攔截`Object.getPrototypeOf(proxy)`,返回一個對象。 **(12)isExtensible(target)** 攔截`Object.isExtensible(proxy)`,返回一個布爾值。 **(13)setPrototypeOf(target, proto)** 攔截`Object.setPrototypeOf(proxy, proto)`,返回一個布爾值。 如果目標對象是函數,那么還有兩種額外操作可以攔截。 **(14)apply(target, object, args)** 攔截Proxy實例作為函數調用的操作,比如`proxy(...args)`、`proxy.call(object, ...args)`、`proxy.apply(...)`。 **(15)construct(target, args, proxy)** 攔截Proxy實例作為構造函數調用的操作,比如new proxy(...args)。 下面是其中幾個重要攔截方法的詳細介紹。 ### get() get方法用于攔截某個屬性的讀取操作。上文已經有一個例子,下面是另一個攔截讀取操作的例子。 ~~~ var person = { name: "張三" }; var proxy = new Proxy(person, { get: function(target, property) { if (property in target) { return target[property]; } else { throw new ReferenceError("Property \"" + property + "\" does not exist."); } } }); proxy.name // "張三" proxy.age // 拋出一個錯誤 ~~~ 上面代碼表示,如果訪問目標對象不存在的屬性,會拋出一個錯誤。如果沒有這個攔截函數,訪問不存在的屬性,只會返回undefined。 利用proxy,可以將讀取屬性的操作(get),轉變為執行某個函數。 ~~~ var pipe = (function () { var pipe; return function (value) { pipe = []; return new Proxy({}, { get: function (pipeObject, fnName) { if (fnName == "get") { return pipe.reduce(function (val, fn) { return fn(val); }, value); } pipe.push(window[fnName]); return pipeObject; } }); } }()); var double = function (n) { return n*2 }; var pow = function (n) { return n*n }; var reverseInt = function (n) { return n.toString().split('').reverse().join('')|0 }; pipe(3) . double . pow . reverseInt . get // 63 ~~~ 上面代碼設置Proxy以后,達到了將函數名鏈式使用的效果。 ### 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 // 報錯 ~~~ 上面代碼中,由于設置了存值函數set,任何不符合要求的age屬性賦值,都會拋出一個錯誤。利用set方法,還可以數據綁定,即每當對象發生變化時,會自動更新DOM。 ### apply() apply方法攔截函數的調用、call和apply操作。 ~~~ var target = function () { return 'I am the target'; }; var handler = { apply: function (receiver, ...args) { return 'I am the proxy'; } }; var p = new Proxy(target, handler); p() === 'I am the proxy'; // true ~~~ 上面代碼中,變量p是Proxy的實例,當它作為函數調用時(p()),就會被apply方法攔截,返回一個字符串。 ### ownKeys() ownKeys方法用來攔截Object.keys()操作。 ~~~ let target = {}; let handler = { ownKeys(target) { return ['hello', 'world']; } }; let proxy = new Proxy(target, handler); Object.keys(proxy) // [ 'hello', 'world' ] ~~~ 上面代碼攔截了對于target對象的Object.keys()操作,返回預先設定的數組。 ### 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實例,就會拋出一個錯誤。 ## Reflect ### 概述 Reflect對象與Proxy對象一樣,也是ES6為了操作對象而提供的新API。Reflect對象的設計目的有這樣幾個。 (1) 將Object對象的一些明顯屬于語言層面的方法,放到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(target, { set: function(target, name, value, receiver) { var success = Reflect.set(target,name, value, receiver); if (success) { log('property '+name+' on '+target+' set to '+value); } return success; } }); ~~~ 上面代碼中,Proxy方法攔截target對象的屬性賦值行為。它采用Reflect.set方法將值賦值給對象的屬性,然后再部署額外的功能。 下面是get方法的例子。 ~~~ var loggedObj = new Proxy(obj, { get: function(target, name) { console.log("get", target, name); return Reflect.get(target, name); } }); ~~~ ### 方法 Reflect對象的方法清單如下。 * Reflect.getOwnPropertyDescriptor(target,name) * Reflect.defineProperty(target,name,desc) * Reflect.getOwnPropertyNames(target) * Reflect.getPrototypeOf(target) * Reflect.deleteProperty(target,name) * Reflect.enumerate(target) * Reflect.freeze(target) * Reflect.seal(target) * Reflect.preventExtensions(target) * Reflect.isFrozen(target) * Reflect.isSealed(target) * Reflect.isExtensible(target) * Reflect.has(target,name) * Reflect.hasOwn(target,name) * Reflect.keys(target) * Reflect.get(target,name,receiver) * Reflect.set(target,name,value,receiver) * Reflect.apply(target,thisArg,args) * Reflect.construct(target,args) 上面這些方法的作用,大部分與Object對象的同名方法的作用都是相同的。下面是對其中幾個方法的解釋。 (1)Reflect.get(target,name,receiver) 查找并返回target對象的name屬性,如果沒有該屬性,則返回undefined。 如果name屬性部署了讀取函數,則讀取函數的this綁定receiver。 ~~~ var obj = { get foo() { return this.bar(); }, bar: function() { ... } } // 下面語句會讓 this.bar() // 變成調用 wrapper.bar() Reflect.get(obj, "foo", wrapper); ~~~ (2)Reflect.set(target, name, value, receiver) 設置target對象的name屬性等于value。如果name屬性設置了賦值函數,則賦值函數的this綁定receiver。 (3)Reflect.has(obj, name) 等同于`name in obj`。 (4)Reflect.deleteProperty(obj, name) 等同于`delete obj[name]`。 (5)Reflect.construct(target, args) 等同于`new target(...args)`,這提供了一種不使用new,來調用構造函數的方法。 (6)Reflect.getPrototypeOf(obj) 讀取對象的__proto__屬性,等同于`Object.getPrototypeOf(obj)`。 (7)Reflect.setPrototypeOf(obj, newProto) 設置對象的__proto__屬性。注意,Object對象沒有對應這個方法的方法。 (8)Reflect.apply(fun,thisArg,args) 等同于`Function.prototype.apply.call(fun,thisArg,args)`。一般來說,如果要綁定一個函數的this對象,可以這樣寫`fn.apply(obj, args)`,但是如果函數定義了自己的apply方法,就只能寫成`Function.prototype.apply.call(fn, obj, args)`,采用Reflect對象可以簡化這種操作。 另外,需要注意的是,Reflect.set()、Reflect.defineProperty()、Reflect.freeze()、Reflect.seal()和Reflect.preventExtensions()返回一個布爾值,表示操作是否成功。它們對應的Object方法,失敗時都會拋出錯誤。 ~~~ // 失敗時拋出錯誤 Object.defineProperty(obj, name, desc); // 失敗時返回false Reflect.defineProperty(obj, name, desc); ~~~ 上面代碼中,Reflect.defineProperty方法的作用與Object.defineProperty是一樣的,都是為對象定義一個屬性。但是,Reflect.defineProperty方法失敗時,不會拋出錯誤,只會返回false。 ## Object.observe(),Object.unobserve() Object.observe方法用來監聽對象(以及數組)的變化。一旦監聽對象發生變化,就會觸發回調函數。 ~~~ var user = {}; Object.observe(user, function(changes){ changes.forEach(function(change) { user.fullName = user.firstName+" "+user.lastName; }); }); user.firstName = 'Michael'; user.lastName = 'Jackson'; user.fullName // 'Michael Jackson' ~~~ 上面代碼中,Object.observer方法監聽user對象。一旦該對象發生變化,就自動生成fullName屬性。 一般情況下,Object.observe方法接受兩個參數,第一個參數是監聽的對象,第二個函數是一個回調函數。一旦監聽對象發生變化(比如新增或刪除一個屬性),就會觸發這個回調函數。很明顯,利用這個方法可以做很多事情,比如自動更新DOM。 ~~~ var div = $("#foo"); Object.observe(user, function(changes){ changes.forEach(function(change) { var fullName = user.firstName+" "+user.lastName; div.text(fullName); }); }); ~~~ 上面代碼中,只要user對象發生變化,就會自動更新DOM。如果配合jQuery的change方法,就可以實現數據對象與DOM對象的雙向自動綁定。 回調函數的changes參數是一個數組,代表對象發生的變化。下面是一個更完整的例子。 ~~~ var o = {}; function observer(changes){ changes.forEach(function(change) { console.log('發生變動的屬性:' + change.name); console.log('變動前的值:' + change.oldValue); console.log('變動后的值:' + change.object[change.name]); console.log('變動類型:' + change.type); }); } Object.observe(o, observer); ~~~ 參照上面代碼,Object.observe方法指定的回調函數,接受一個數組(changes)作為參數。該數組的成員與對象的變化一一對應,也就是說,對象發生多少個變化,該數組就有多少個成員。每個成員是一個對象(change),它的name屬性表示發生變化源對象的屬性名,oldValue屬性表示發生變化前的值,object屬性指向變動后的源對象,type屬性表示變化的種類。基本上,change對象是下面的樣子。 ~~~ var change = { object: {...}, type: 'update', name: 'p2', oldValue: 'Property 2' } ~~~ Object.observe方法目前共支持監聽六種變化。 * add:添加屬性 * update:屬性值的變化 * delete:刪除屬性 * setPrototype:設置原型 * reconfigure:屬性的attributes對象發生變化 * preventExtensions:對象被禁止擴展(當一個對象變得不可擴展時,也就不必再監聽了) Object.observe方法還可以接受第三個參數,用來指定監聽的事件種類。 ~~~ Object.observe(o, observer, ['delete']); ~~~ 上面的代碼表示,只在發生delete事件時,才會調用回調函數。 Object.unobserve方法用來取消監聽。 ~~~ Object.unobserve(o, observer); ~~~ 注意,Object.observe和Object.unobserve這兩個方法不屬于ES6,而是屬于ES7的一部分。不過,Chrome瀏覽器從33版起就已經支持。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看