ECMAScript 6教程 (二) 物件和函式
物件
屬性的簡潔表示法
ES6允許直接寫入變數和函式,作為物件的屬性和方法。這樣的書寫更加簡潔。
function f( x, y ) {
return { x, y };
}
// 等同於
function f( x, y ) {
return { x: x, y: y };
}
示例:
var Person = {
name: '張三',
birth:'1990-01-01',
// 等同於hello: function ()...
hello() { document.write('我的名字是', this.name); }
};
Person.hello();
這種寫法用於函式的返回值,將會非常方便。
function getPoint() {
var x = 1;
var y = 10;
return {x, y};
}
getPoint() // {x:1, y:10}
屬性名錶達式
JavaScript語言定義物件的屬性,有兩種方法。
let obj = {};
// 方法一
obj.foo = true;
// 方法二
obj['a'+'bc'] = 123;
document.write(obj);
上面程式碼的方法一是直接用識別符號作為屬性名,方法二是用表示式作為屬性名,這時要將表示式放在方括號之內。
如果使用字面量方式定義物件(使用大括號),在ES5中只能使用方法一(識別符號)定義屬性。
var obj = {
foo: true,
abc: 123
};
ES6允許字面量定義物件時,用方法二(表示式)作為物件的屬性名,即把表示式放在方括號內。
let propKey = 'foo';
let obj = {
[propKey]: true,
['a'+'bc']: 123
};
表示式還可以用於定義方法名。
let obj = {
['h'+'ello']() {
return 'hi';
}
};
document.write(obj.hello()); // hi
比較兩個值是否嚴格相等
Object.is()用來比較兩個值是否嚴格相等。它與嚴格比較運算子(===)的行為基本一致,不同之處只有兩個:一是+0不等於-0,二是NaN等於自身。
+0 === -0 //true
NaN === NaN // false
Object.is(+0, -0) // false
Object.is(NaN, NaN) // true
源物件的所有可列舉屬性,複製到目標物件
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}
proto屬性
proto屬性,用來讀取或設定當前物件的prototype物件。該屬性一度被正式寫入ES6草案,但後來又被移除。目前,所有瀏覽器(包括IE11)都部署了這個屬性。
// es6的寫法
var obj = {
__proto__: someOtherObj,
method: function() { ... }
}
// es5的寫法
var obj = Object.create(someOtherObj);
obj.method = function() { ... }
Symbol型別
ES6引入了一種新的原始資料型別Symbol,表示獨一無二的ID。凡是屬性名屬於Symbol型別,就都是獨一無二的,可以保證不會與其他屬性名產生衝突。
let s = Symbol();
typeof s
// "symbol"
typeof運算子的結果,表明變數s是Symbol資料型別,而不是字串之類的其他型別。
注意,Symbol函式前不能使用new命令,否則會報錯。這是因為生成的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)'
內建代理
Proxy 內建的一個代理工具,使用他可以在物件處理上加一層屏障:
S6原生提供Proxy建構函式,用來生成Proxy例項。
var proxy = new Proxy(target, handler)
new Proxy()表示生成一個Proxy例項,它的target參數列示所要攔截的目標物件,handler引數也是一個物件,用來定製攔截行為。
var plain = {
name : "hubwiz"
};
var proxy = new Proxy(plain, {
get: function(target, property) {
return property in target ? target[property] : "匯智網";
}
});
proxy.name // "hubwiz"
proxy.title // "匯智網"
Proxy(target, handler), 這裡的 handler有如下的方法:
- get(target, propKey,receiver):攔截物件屬性的讀取,比如proxy.foo和proxy['foo'],返回型別不限。最後一個引數receiver可選,當target物件設定了propKey屬性的get函式時,receiver物件會繫結get函式的this物件。
- set(target, propKey, value, receiver):攔截物件屬性的設定,比如proxy.foo = v或proxy['foo'] = v,返回一個布林值。
- has(target, propKey):攔截propKey in proxy的操作,返回一個布林值。
- deleteProperty(target, propKey) :攔截delete proxy[propKey]的操作,返回一個布林值。
- enumerate(target):攔截for (var x in proxy),返回一個遍歷器。
- hasOwn(target, propKey):攔截proxy.hasOwnProperty('foo'),返回一個布林值。
- ownKeys(target):攔截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy),返回一個陣列。該方法返回物件所有自身的屬性,而Object.keys()僅返回物件可遍歷的屬性。
- getOwnPropertyDescriptor(target, propKey) :攔截Object.getOwnPropertyDescriptor(proxy, propKey),返回屬性的描述物件。
- defineProperty(target, propKey, propDesc):攔截Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一個布林值。
- preventExtensions(target):攔截Object.preventExtensions(proxy),返回一個布林值。
- getPrototypeOf(target) :攔截Object.getPrototypeOf(proxy),返回一個物件。
- isExtensible(target):攔截Object.isExtensible(proxy),返回一個布林值。
- setPrototypeOf(target, proto):攔截Object.setPrototypeOf(proxy, proto),返回一個布林值。
如果目標物件是函式,那麼還有兩種額外操作可以攔截。
- apply(target, object, args):攔截Proxy例項作為函式呼叫的操作,比如proxy(...args)、roxy.call(object, ...args)、proxy.apply(...)。
- construct(target, args, proxy):攔截Proxy例項作為建構函式呼叫的操作,比如new proxy(...args)。
函式
預設引數
現在可以在定義函式的時候指定引數的預設值了,而不用像以前那樣通過邏輯或操作符來達到目的了。
function sayHello(name){
//傳統的指定預設引數的方式
var name = name||'hubwiz';
document.write('Hello '+name);
}
//運用ES6的預設引數
function sayHello2(name='hubwiz'){
document.write(`Hello ${name}`);
}
sayHello(); //輸出:Hello hubwiz
sayHello('匯智網'); //輸出:Hello 匯智網
sayHello2(); //輸出:Hello hubwiz
sayHello2('匯智網'); //輸出:Hello 匯智網
rest引數
rest引數(形式為“...變數名”)可以稱為不定引數,用於獲取函式的多餘引數,這樣就不需要使用arguments物件了。
rest引數搭配的變數是一個陣列,該變數將多餘的引數放入陣列中。
function add(...values) {
let sum = 0;
for (var val of values) {
sum += val;
}
return sum;
}
add(1, 2, 3) // 6
不定引數的格式是三個句點後跟代表所有不定引數的變數名。比如以上示例中,...values 代表了所有傳入add函式的引數。
擴充套件運算子
擴充套件運算子(spread)是三個點(...)。它好比rest引數的逆運算,將一個陣列轉為用逗號分隔的引數序列。該運算子主要用於函式呼叫。
它允許傳遞陣列或者類陣列直接做為函式的引數而不用通過apply。
var people=['張三','李四','王五'];
//sayHello函式本來接收三個單獨的引數people1,people2和people3
function sayHello(people1,people2,people3){
document.write(`Hello ${people1},${people2},${people3}`);
}
//但是我們將一個陣列以擴充引數的形式傳遞,它能很好地對映到每個單獨的引數
sayHello(...people); //輸出:Hello 張三,李四,王五
//而在以前,如果需要傳遞陣列當引數,我們需要使用函式的apply方法
sayHello.apply(null,people); //輸出:Hello 張三,李四,王五
箭頭函式
箭頭函式是使用=>語法的函式簡寫形式。這在語法上與 C#、Java 8 和 CoffeeScript 的相關特性非常相似。
var array = [1, 2, 3];
//傳統寫法
array.forEach(function(v, i, a) {
document.write(v);
});
//ES6
array.forEach(v => document.write(v));
它們同時支援表示式體和語句體。與(普通的)函式所不同的是,箭頭函式和其上下文中的程式碼共享同一個具有詞法作用域的this。
var evens = [1,2,3,4,5];
var fives = [];
// 表示式體
var odds = evens.map(v => v + 1);
var nums = evens.map((v, i) => v + i);
var pairs = evens.map(v => ({even: v, odd: v + 1}));
// 語句體
nums.forEach(v => {
if (v % 5 === 0)
fives.push(v);
});
document.write(fives);
// 具有詞法作用域的 this
var bob = {
_name: "Bob",
_friends: ["Amy", "Bob", "Cinne", "Dylan", "Ellen"],
printFriends() {
this._friends.forEach(f =>
document.write(this._name + " knows " + f));
}
}
bob.printFriends();
箭頭函式有幾個使用注意點。
- 函式體內的this物件,繫結定義時所在的物件,而不是使用時所在的物件。
- 不可以當作建構函式,也就是說,不可以使用new命令,否則會丟擲一個錯誤。
- 不可以使用arguments物件,該物件在函式體內不存在。
上面三點中,第一點尤其值得注意。this物件的指向是可變的,但是在箭頭函式中,它是固定的。
函式繫結
函式繫結運算子是並排的兩個雙引號(::),雙引號左邊是一個物件,右邊是一個函式。該運算子會自動將左邊的物件,作為上下文環境(即this物件),繫結到右邊的函式上面。
let log = ::console.log;
// 等同於
var log = console.log.bind(console);
foo::bar;
// 等同於
bar.call(foo);
foo::bar(...arguments);
i// 等同於
bar.apply(foo, arguments);
尾呼叫優化
什麼是尾呼叫? 尾呼叫的概念非常簡單,一句話就能說清楚,就是指某個函式的最後一步是呼叫另一個函式。
function f(x){
return g(x);
}
上面程式碼中,函式f的最後一步是呼叫函式g,這就叫尾呼叫。
以下三種情況,都不屬於尾呼叫。
// 情況一
function f(x){
let y = g(x);
return y;
}
// 情況二
function f(x){
return g(x) + 1;
}
// 情況三
function f(x){
g(x);
}
以上的示例中,情況一、二是呼叫函式g之後,有其他操作。情況三等同於下面的程式碼。
function f(x){
g(x);
return undefined;
}
尾呼叫由於是函式的最後一步操作,所以不需要保留外層函式的呼叫記錄,因為呼叫位置、內部變數等資訊都不會再用到了,只要直接用內層函式的呼叫記錄,取代外層函式的呼叫記錄就可以了。
function f() {
let m = 1;
let n = 2;
return g(m + n);
}
f();
// 等同於
function f() {
return g(3);
}
f();
// 等同於
g(3);
上面程式碼中,如果函式g不是尾呼叫,函式f就需要儲存內部變數m和n的值、g的呼叫位置等資訊。但由於呼叫g之後,函式f就結束了,所以執行到最後一步,完全可以刪除 f(x) 的呼叫幀,只保留g(3) 的呼叫幀。
“尾呼叫優化”(Tail call optimization),即只保留內層函式的呼叫幀,這樣可以節省記憶體。
同步課程在匯智網可以找到:http://www.hubwiz.com/course/5594e91ac086935f4a6fb8ef/
相關文章
- ECMAScript 6教程 (三)
- 深入掌握 ECMAScript 6 非同步程式設計(二):Thunk 函式的含義和用法非同步程式設計函式
- ECMAScript6 教程(一)
- 類函式和物件函式 PHP函式物件PHP
- Kotlin教程(二)函式Kotlin函式
- js的函式和物件JS函式物件
- ES6-Generator 函式 和 async 函式函式
- ECMAScript——(二)
- ES6語法(二) 函式函式
- ECMAScript正規表示式6個最新特性
- 《JavaScript物件導向精要》之二:函式JavaScript物件函式
- 例項物件和函式物件的區別物件函式
- JavaScript非同步程式設計(1)- ECMAScript 6的Promise物件JavaScript非同步程式設計Promise物件
- 玩轉ECMAScript 6 ~
- ECMAScript 6 掃盲
- 函式物件、物件、原型函式物件原型
- 深入掌握 ECMAScript 6 非同步程式設計(一):Generator 函式的含義與用法非同步程式設計函式
- 深入掌握 ECMAScript 6 非同步程式設計(四):async函式的含義與用法非同步程式設計函式
- Javascript的函式式和麵向物件特性JavaScript函式物件
- ES6學習筆記(三)【函式,物件】筆記函式物件
- C++ 常物件和常函式C++物件函式
- Python 快速教程(進階篇07):函式物件Python函式物件
- C++中的函式指標和函式物件總結C++函式指標物件
- 深入掌握 ECMAScript 6 非同步程式設計(三):co函式庫的含義與用法非同步程式設計函式
- 函式式JavaScript(3):.apply()、.call() 和arguments物件函式JavaScriptAPP物件
- XSL函式二----DOM中物件的方法 (轉)函式物件
- JavaFX教程-函式、陣列、表示式和操作Java函式陣列
- ECMAScript 6筆記(一)筆記
- ECMAScript6簡介
- #ECMASCRIPT6筆記筆記
- Js函式和物件學習體會JS函式物件
- 從__proto__和prototype詳解物件和函式物件函式
- ES6深入學習(二)關於函式函式
- ECMAScript 5.1 物件(Object)與原型物件Object原型
- Javascript 物件導向中的建構函式和原型物件JavaScript物件函式原型
- TypeScript(6)函式TypeScript函式
- JavaScript物件導向之二(建構函式繼承)JavaScript物件函式繼承
- JS物件導向程式設計(二):建構函式JS物件程式設計函式