ES6+新特性說明
ES全稱ECMAScript,ECMAScript是ECMA制定的標準化腳本語言。目前JavaScript使用的ECMAScript版本爲ECMA-417。關於ECMA的最新資訊可以瀏覽 ECMA news查看。
一、ES6新特性(2015)
ES6的特性比較多,在 ES5 發佈近 6 年(2009-11 至 2015-6)之後纔將其標準化。兩個發佈版本之間時間跨度很大,所以ES6中的特性比較多。 在這裏列舉幾個常用的:
- Let與Const
- 類
- 模塊化
- 箭頭函數
- 函數參數默認值
- 模板字符串
- 解構賦值
- 延展操作符
- 對象屬性簡寫
- Promise
- Map 與 Set
- function*
- Object.defineProperty()
1. Let與Const
(1)let
let聲明的變量,塊級變量,聲明的對象具有塊級作用域。
四大特性:
-
塊級作用域:{}下起作用。
-
同級作用域下,只能存在一個(let聲明的變量不能重複聲明)。
-
不能變量提升。
-
let 在循環中經常被使用(重點),i變量只在for()內有效,不污染其他區域。
實例:
let a = 3;
if(1){
console.log(a); // 結果:error
//注意:塊級變量不會變量提升
let a = 4;
}
console.log(a); // 結果:3
let a = 1;
let a = 2;
console.log(a); // 結果:error
//i變量只在for()內有效,不污染其他區域
var arr = ['apple', 'banana', 'peal'];
for (let i = 0; i < arr.length; i++) {
}
console.log(i); // 報錯:ReferenceError: i is not defined
(2)const
const聲明的變量稱爲常量,即不可改變其值的量。
四大特性:
-
不能修改常量值。
-
不能重複聲明。
-
聲明後必須賦值,因爲const聲明的常量不能被修改。
-
常量不可改的是其地址,聲明obj對象就會生成指向其內容的地址(不可修改),但是可以修改其內容中的屬性。
實例:
const i = 20;
console.log(i); //=> 20
/* 不能修改常量值 */
// i = 30;
// console.log(i); //=> error
/* 不能重複聲明 */
// const i = 30;
// console.log(i); //=> error
/* 聲明後必須賦值 */
// const c;
// console.log(c); //=> error
/* 不能改變地址指向 */
const obj = {name:'yaodao',age:20};
obj.name = 'yaodao2';
console.log(obj); //=> { name: 'yaodao2', age: 20 }
obj = 2;
console.log(obj); //=> error
2. 類
ES6 引入了class(類),讓JavaScript的面向對象編程變得更加簡單和易於理解。
class Person {
/* 構造器 */
constructor(name, sex, age) {
this.name = name;
this.sex = sex;
this.age = age;
}
/* 原型 */
walk() {
if (this.age <= 2) {
return console.log('我不會走路');
}
if (this.age >2 && this.age < 4) {
return console.log('我會走路了');
}
return console.log('走路');
}
study(skill) {
console.log('學習' + skill);
}
introduce() {
console.log(`我是${this.name},我是一個${this.sex === 'male' ? "男" : "女"}孩,今年${this.age}歲了。`);
}
}
/* 繼承類 */
class Boy extends Person { // extends關鍵字繼承
/* 構造器 */
constructor(name, age) {
/* super語法可以調用父對象上的函數 */
// 子類必須要在constructor中指定super 函數,否則在新建實例的時候會報錯。
// 如果沒有置頂consructor,默認帶super函數的constructor將會被添加。
super('湯姆', 'male', 14);
}
study(e) {
console.log(super.study(e));
}
}
var boy = new Boy();
boy.introduce(); // 我是湯姆,我是一個男孩,今年 14 歲了。
boy.study('English');//學習English
console.log(boy instanceof Boy);// true
console.log(boy instanceof Person);//true
3. 模塊化
ES5不支持原生的模塊化,在ES6中模塊作爲重要的組成部分被添加進來。模塊的功能主要由 export 和 import 組成。每一個模塊都有自己單獨的作用域,模塊之間的相互調用關係是通過 export 來規定模塊對外暴露的接口,通過import來引用其它模塊提供的接口。同時還爲模塊創造了命名空間,防止函數的命名衝突。
(1)export(導出)
ES6允許在一個模塊中使用export來導出多個變量或函數。
導出變量
exportVar.js:
var a = 10;
var b = 20;
/* 導出變量 */
export {a,b};
導出常量
export const sqrt =Math.sqrt;
導出函數
export function fn(e){
return e;
}
(2)import(導入)
importVar.js:
定義好模塊的輸出以後就可以在另外一個模塊通過import引用。
導入變量
import {a,b} from './exportVar.js'
導入常量
import {sqrt} from './exportVar.js'
導入函數
import './exportVar.js';
console.log(fn('hello'));
擴展:Module看import和require區別
import和require,開發中一定不少見,尤其是需要前端工程化的項目現在都已經離不開node了,在node環境下這兩者都是大量存在的,大體上來說他們都是爲了實現JS代碼的模塊化,那爲什麼會出現兩種方案呢,又有什麼不同呢?
模塊化的不同解決方案
requirejs遵循AMD,seajs遵循CMD,node的module遵循CommonJS規範,雖然寫法上有所不同,都是爲了能夠間接實現模塊化的基礎上保持較爲一致的代碼風格。
隨着ES2015的發佈,官方標準定義了一種模塊化的方案,那就是import、export。可是,標準畢竟是標準,各大瀏覽器和node終端要實現標準還是有一段距離的,目前來說都2018年了主流瀏覽器都還沒實現,還得依賴轉換工具(例如babel)轉爲ES5的代碼之後瀏覽器才能解析。所以這也就解釋了爲什麼我們的工程化代碼中nodeJS遵循的CommonJS規範和ES6的模塊化方案並存的現象。
兩者的區別:
- import是ES6標準中的模塊化解決方案,require是node中遵循CommonJS規範的模塊化解決方案。
- 後者支持動態引入,也就是require(${path}/xx.js),前者目前不支持,但是已有提案。
- 前者是關鍵詞,後者不是。
- 前者是編譯時加載,必須放在模塊頂部,在性能上會比後者好一些,後者是運行時加載,理論上來說放在哪裏都可以。
- 前者採用的是實時綁定方式,即導入和導出的值都指向同一個內存地址,所以導入值會隨着導出值變化。而後者在導出時是指拷貝,就算導出的值變化了,導入的值也不會變化,如果想要更新值就要重新導入。
- 前者會編譯成require/exports來執行。
require/exports用法:
導出變量
// test.js
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;
module.exports = { firstName, lastName, year };
引入變量
// demo.js
const test = require('./test.js');
console.log(test); // {firstName: "Michael", lastName: "Jackson", year: 1958}
其他導出引入內容同上…
總結
import和require就是兩種不同的JS模塊化實現方式而已,由於之前npm生態的很多包都是基礎CommonJS規範寫的,所以相當一段時間之內必然是import和require這兩種模塊引入方式共存的。
總體來說時代總是發展的,ES6作爲語言規範是遲早會被各大主流瀏覽器支持的,不然也就稱不上主流瀏覽器了。所以爲了長遠考慮,我們還是儘量使用ES6的import來引入模塊,等以後瀏覽器支持了我們也就可以少改一些代碼了。
4. 箭頭(Arrow)函數
箭頭函數的箭頭=>
之前是一個空括號、單個的參數名、或用括號括起的多個參數名,而箭頭之後可以是一個表達式(作爲函數的返回值),或者是用花括號括起的函數體(需要自行通過return來返回值,否則返回的是undefined)。
實例用法:
var fn1 = function(s1,s2){
return s1+s2;
}
// ES6中的新聲明函數方式:箭頭函數
var fn2 = (s1,s2) =>{
return s1+s2;
}
console.log(fn1(1,2)); //=> 3
console.log(fn2(1,2)); //=> 3
使用技巧:
1)有一個參數,括號可以省略。
var fn = s =>{
//內容...
}
2)沒有一個參數,一定要有括號。
var fn = ()=>{
//內容...
}
3)多於一個參數,一定要用逗號隔開。
var fn = (s1,s2,s3)=>{
//內容...
}
4)如果方法體只有一句代碼,可以省略{},如果是返回值可以省略{}----侷限性比較大。
var fn = (s1,s2) => s1+s2;
fn(1,2);
var fn = (s1,s2) => console.log('yaodao');
5)如果方法體不止一句代碼,就不可以省略,每句代碼需要用;隔開。
var fn = s =>{
var a = 2;
var b = a+s;
console.log(b);
}
fn(2); //=> 4
注意點:
-
箭頭函數沒有自己的this,函數內部寫的this,指的是外層代碼塊中的this,所以在箭頭函數內部不要使用this。
-
this指向定義時的對象,也就是說箭頭函數一旦定義完成,它的指向是固定的,沒法改變,它的指向是定義時所在的作用域,而不是執行時的作用域。
-
箭頭函數不能用作構造函數。
-
箭頭函數內部不存在arguments,箭頭函數體中arguments其實指向的是外層函數的arguments。
var obj1 = {
name:'yaodao',
fn:()=>{
console.log(this.name);
console.log(this);
},
}
obj1.fn();
結果:
個人總結:
1.箭頭函數的this指向是定義(聲明)時就綁定的,和執行無關
2.箭頭函數沒有自己的this,繼承了當前所在環境執行時的this指向
遇到箭頭函數解題:
-
看當前箭頭函數定義的環境是什麼?( 小技巧:找上一個{})
-
遇到箭頭函數的執行或調用,忽略,不看,對箭頭函數this指向沒有影響
-
判斷當前環境執行時this指向誰,箭頭函數的this就指向誰
比如:
var url = "window";
function Antzone() {
let func = () => {
console.log(this.url);
}
func();
}
Antzone();
解析:箭頭沒有自己的this指向,它是定義在Antzone()函數中,當Antzone()調用的時候,this指向window,所以打印是window。
var name = 'window';
var obj = {
name: 'obj',
say: function () {
setTimeout(function () {
var b2 = () => this.name;
console.log(b2());
}, 100);
}
}
obj.say();
解析:b2()調用這個的時候,由於是箭頭函數,沒有自己的this,所以往上找,看他定義的環境,他是定義在延時器中,延時器中的this指向window,所以打印是window。
5. 函數參數默認值
使用默認值寫法:
function foo(width=20,color='red'){
//...
}
不使用默認值:
function foo(width,color){
var width = width||20;
var color = color||'red';
//...
}
這樣寫一般沒問題,但當 參數的布爾值爲false
時,就會有問題了。比如,我們這樣調用foo函數:
foo(0,'');
因爲 0的布爾值爲false
,這樣height的取值將是50。同理color的取值爲‘red’。
所以說, 函數參數默認值
不僅能是代碼變得更加簡潔而且能規避一些問題。
6. 模板字符串
ES6支持模板字符串,使得字符串的拼接更加的簡潔、直觀。
不使用模板字符串:
var first = 'hello';
var last = 'world';
var str = 'I use '+first+' '+last+'!!!'
console.log(str); //=> I use hello world!!!
使用模板字符串:
var first = 'hello';
var last = 'world';
var str = `I use ${first} ${last} !!!`;
console.log(str); //=> I use hello world!!!
在ES6中通過 ${}
就可以完成字符串的拼接,只需要將變量放在大括號之中。
解構賦值語法是JavaScript的一種表達式,可以方便的從數組或者對象中快速提取值賦給定義的變量。
7. 解構賦值
簡而言之就是解析對象結構,對應賦值。
解構賦值語法是JavaScript的一種表達式,可以方便的從數組或者對象中快速提取值賦給定義的變量。
(1)獲取數組中的值
從數組中獲取值並賦值到變量中,變量的順序與數組中對象順序對應。
var foo = ["one", "two", "three", "four"];
var [one, two, three] = foo;
console.log(one); // "one"
console.log(two); // "two"
console.log(three); // "three"
//如果你要忽略某些值,你可以按照下面的寫法獲取你想要的值
var [first, , , last] = foo;
console.log(first); // "one"
console.log(last); // "four"
//或者可以這樣
var a, b; //先聲明變量
[a, b] = [1, 2];
console.log(a); // 1
console.log(b); // 2
如果沒有從數組中的獲取到值,你可以爲變量設置一個默認值。 (會依照次序賦值)
var a, b;
[a=1, b=2] = [10];
console.log(a); // 10
console.log(b); // 2
通過解構賦值可以方便的交換兩個變量的值。
var a = 10;
var b = 20;
[a, b] = [b, a];
console.log(a); // 20
console.log(b); // 10
(2)獲取字符串中的字符
// 字符串的解構賦值
let [w,e,r] = 'che';
console.log(w);// "c"
console.log(e);// "h"
console.log(r);// "e"
(3)獲取對象中的屬性值
const student = {
name:'yaodao',
age:20,
city:'Guangzhou'
};
const {name,age,city} = student;
console.log(name); // "yaodao"
console.log(age); // 20
console.log(city); // "Guangzhou"
8. 延展操作符("模式匹配"賦值)
延展操作符...
可以在函數調用/數組構造時, 將數組表達式或者string在語法層面展開;還可以在構造對象時, 將對象表達式按key-value的方式展開。
簡單例子:
let [i,...p] = ['1','2','3','4','5']
console.log(i);
console.log(...p);
(1)用法
數組構造或字符串構造:
let [i,...p] = ['1','2','3','4','5']
深淺拷貝:
參考博客: https://blog.csdn.net/Errrl/article/details/104034726
(2)應用場景
在函數調用時使用延展操作符
function sum(x, y, z) {
return x + y + z;
}
const numbers = [1, 2, 3];
//不使用延展操作符
console.log(sum.apply(null, numbers));//6
//使用延展操作符
console.log(sum(...numbers));// 6
構造數組
沒有展開語法的時候,只能組合使用 push,splice,concat 等方法,來將已有數組元素變成新數組的一部分。有了展開語法, 構造新數組會變得更簡單、更優雅:
const stu = ['laoda','laoer'];
const school = [...stu,'laosan','laosi'];
console.log(school); //=> ["laoda", "laoer", "laosan", "laosi"]
和參數列表的展開類似, ...
在構造字數組時, 可以在任意位置多次使用。
深淺拷貝
/* 對象的拷貝 */
var sObj = {
name: 'chen',
age: 10
};
var cObj = {...sObj};
cObj.name= 'yaodao';
console.log(sObj); //=> {name: 'yaodao', age: 10}
/* 數組的拷貝 */
var arr = [1,2,3]
var res= [...arr]
res[0] = 10;
console.log(arr); //=> [1, 2, 3]
console.log(res); //=> [10, 2, 3]
連接數組、對象
/* 連接數組 */
var arr = [1,2,3];
var arr2 = [4,5,6];
var res = [...arr,...arr2];
console.log(res); //=> [1, 2, 3, 4, 5, 6]
/* 連接對象 */
var obj = {name:'chen'};
var obj2 = {age:20};
var res = {...obj,...obj2};
console.log(res); //=> {name:'chen',age:20}
9. 對象屬性簡寫
在ES6中允許我們在設置一個對象的屬性的時候不指定屬性名。
沒有使用ES6
const name='yaodao',age= 20,sex='male';
const introduct = {
name:name,
age:age,
sex:sex
};
console.log(introduct);//{name: "yaodao", age: 20, sex: "male"}
明顯重複很多操作,顯得非常累贅。
使用ES6
const name='yaodao',age= 20,sex='male';
const introduct = {
name,
age,
sex
};
console.log(introduct);//{name: "yaodao", age: 20, sex: "male"}
對象中直接寫變量,非常簡潔明瞭。
10. Promise
Promise 是異步編程的一種解決方案,比傳統的解決方案callback更加的優雅。它最早由社區提出和實現的,ES6 將其寫進了語言標準,統一了用法,原生提供了Promise對象。
舉個簡單的例子:
回調地獄:
/* 嵌套兩個setTimeout回調函數 */
setTimeout(function(){
console.log('I'); // 1秒後輸出"I"
setTimeout(function(){
console.log('use'); // 2秒後輸出"am"
setTimeout(function(){
console.log('hello'); // 3秒後輸出"use"
setTimeout(function(){
console.log('world'); // 4秒後輸出"use"
//...回調地獄
}, 1000);
}, 1000);
}, 1000);
}, 1000);
使用Promise:
/* 封裝一個promise函數 */
function fn(){
return new Promise(function(resolve,reject){
setTimeout(resolve,1000);
});
};
//鏈式調用
//注意每一個then一點要return函數fn。
fn()
.then(function(){
console.log('I');
return fn();
})
.then(function(){
console.log('use');
return fn();
})
.then(function(){
console.log('hello');
return fn();
})
.then(function(){
console.log('world');
})
上面的的代碼使用四個.then來進行異步編程串行化,避免了回調地獄
11. Map 與 Set
JavaScript的默認對象表示方式{}
可以視爲其他語言中的Map
或Dictionary
的數據結構,即一組鍵值對。
但是JavaScript的對象有個小問題,就是鍵必須是字符串。但實際上Number或者其他數據類型作爲鍵也是非常合理的。
爲了解決這個問題,最新的ES6規範引入了新的數據類型Map
。
(1)Map
Map
是一組鍵值對的結構,具有極快的查找速度。
舉個例子,假設要根據同學的名字查找對應的成績,如果用Array
實現,需要兩個Array
:
var names = ['Michael', 'Bob', 'Tracy'];
var scores = [95, 75, 85];
給定一個名字,要查找對應的成績,就先要在names中找到對應的位置,再從scores取出對應的成績,Array越長,耗時越長。
如果用Map實現,只需要一個“名字”-“成績”的對照表,直接根據名字查找成績,無論這個表有多大,查找速度都不會變慢。用JavaScript寫一個Map如下:
var m = new Map([['Michael', 95],['Bob', 75], ['Tracy', 85]]);
m.get('Michael'); // 95
初始化Map
需要一個二維數組,或者直接初始化一個空Map
。Map
具有以下方法:
- set(); 設置鍵值對
- has(); 是否存在鍵值對
- get(); 獲取鍵值對
- delete(); 刪除鍵值對
var m = new Map(); //創建Map
m.set('Adam', 67); //設置key-value
m.set('Bob', 59);
m.has('Adam'); //是否存在key 'Adam': true
m.get('Adam'); //獲取鍵值,輸出67
m.delete('Adam'); //刪除key 'Adam'
m.get('Adam'); //undefined
由於一個key只能對應一個value,所以,多次對一個key放入value,後面的值會把前面的值沖掉:
var m = new Map();
m.set('Adam', 67);
m.set('Adam', 88);
m.get('Adam'); // 88
(2)Set
Set
和Map
類似,也是一組key的集合,但不存儲value。由於key不能重複,所以,在Set
中,沒有重複的key。(達到數組去重的作用)
要創建一個Set
,需要提供一個Array
作爲輸入,或者直接創建一個空Set
:
var s1 = new Set();
var s2 = new Set([1, 2, 3]);
重複元素在Set
中自動被過濾:
var s = new Set([1, 2, 3, 3, '3']);
console.log(s); //=> Set(4) {1, 2, 3, "3"}
通過add(key)
方法可以添加元素到Set
中,可以重複添加,但不會有效果,原因就是key不能重複。
var s = new Set([1, 2, 3, 3, '3']);
s.add(4);
console.log(s); // Set(4) {1, 2, 3, 4}
s.add(4);
console.log(s); // 仍然是 Set {1, 2, 3, 4}
通過delete(key)
方法可以刪除元素:
var s = new Set([1, 2, 3]);
console.log(s); // Set(3) {1, 2, 3}
s.delete(3);
console.log(s); // Set(2) {1, 2}
12. function*
function*
這種聲明方式(function
關鍵字後跟一個星號)會定義一個生成器函數 (generator function),它返回一個 Generator
對象。
語法:
function* name([param[, param[, ... param]]]) { statements }
定義:
生成器函數在執行時能暫停,後面又能從暫停處繼續執行。
調用一個生成器函數並不會馬上執行它裏面的語句,而是返回一個這個生成器的 迭代器 ( iterator)對象。當這個迭代器的 next()
方法被首次(後續)調用時,其內的語句會執行到第一個(後續)出現 yield 關鍵字的位置爲止,yield後緊跟迭代器要返回的值。或者如果用的是 yield*(多了個星號),則表示將執行權移交給另一個生成器函數(當前生成器暫停執行)。注意:作爲一個生成器函數所以不能被當作構造器使用。
next()
方法返回一個對象,這個對象包含兩個屬性:value 和 done,value 屬性表示本次 yield
表達式的返回值,done 屬性爲布爾類型,表示生成器後續是否還有yield
語句,即生成器函數是否已經執行完畢並返回。
調用 next()
方法時,如果傳入了參數,那麼這個參數會傳給上一條執行的 yield語句左邊的變量,例如下面例子中的x
:
function *iter(){
yield 10;
x=yield 'hello';
yield x;
}
var iter_obj=iter();
console.log(iter_obj.next());// 執行 yield 10,返回 10
console.log(iter_obj.next());// 執行 yield 'hello',返回 'hello'
/* 傳參與賦值 */
console.log(iter_obj.next(100));// 將 100 賦給上一條 yield 'foo' 的左值,即執行 x=100,返回 100
console.log(iter_obj.next());// 執行完畢,value 爲 undefined,done 爲 true
yield*講解:
表示將執行權移交給另一個生成器函數(當前生成器暫停執行)。
function* otherIter(i) {
yield i + 1;
yield i + 2;
yield i + 3;
}
function* capitalIter(i){
yield i;
yield* otherIter(i);// 將執行移交給otherIter,暫停後面的yield
yield i + 10;
}
var cap = capitalIter(10);
console.log(cap.next().value); // 10
console.log(cap.next().value); // 11
console.log(cap.next().value); // 12
console.log(cap.next().value); // 13
console.log(cap.next().value); // 20
顯性返回:
只要存在return就稱爲顯性返回,其後的yield將無法被next()。
function* iter() {
yield "1";
return "2"; //顯式返回處,可以觀察到 done 也立即變爲了 true
yield "不被執行了";// 不會被執行了
}
var it = iter()
console.log(it.next()); // { value: "1", done: false }
console.log(it.next()); // { value: "2", done: true }
console.log(it.next()); // { value: undefined, done: true }
使用迭代器將數組降維:
/* 迭代器(利用遞歸,yield*轉移執行權) */
function* iterArr(arr) {
if (Array.isArray(arr)) {
for(let i=0; i < arr.length; i++) {
yield* iterArr(arr[i]);
}
} else {
yield arr;
}
}
測試:
var arr = ['a', ['b', 'c'], ['d', 'e']];
for(var x of iterArr(arr)) {
console.log(x); //=> a b c d e
}
/* 獲取一維數組 */
/* 直接使用延展操作符操作 */
var arr = ['a', ['b', 'c'], ['d', 'e']];
var iter = iterArr(arr);
arr = [...iter]; //=> ["a", "b", "c", "d", "e"]
13. Object.defineProperty()(重點)
Object.defineProperty()
方法會直接在一個對象上定義一個新屬性,或者修改一個對象的現有屬性, 並返回這個對象。
語法:
Object.defineProperty(obj, prop, descriptor);
參數定義與說明:
-
obj 要在其上定義屬性的對象。
-
prop 要定義或修改的屬性的名稱。
-
descriptor 將被定義或修改的屬性描述符。
該方法允許精確添加或修改對象的屬性。通過賦值操作添加的普通屬性是可枚舉的,能夠在屬性枚舉期間呈現出來(for…in或 Object.keys)。這個方法允許修改默認的額外選項(或配置)。默認情況下,使用 Object.defineProperty()
添加的屬性值是不可修改的。
descriptor屬性描述符
對象裏目前存在的屬性描述符有兩種主要形式:數據描述符和存取描述符。數據描述符是一個具有值的屬性,該值可能是可寫的,也可能不是可寫的。存取描述符是由getter-setter函數對描述的屬性。描述符必須是這兩種形式之一;不能同時是兩者。
數據描述符和存取描述符均具有以下可選鍵值(默認值是在使用Object.defineProperty()定義屬性的情況下):
-
configurable
(可配置)當且僅當該屬性的 configurable 爲 true 時,該屬性描述符
才能夠被改變,同時該屬性也能從對應的對象上被刪除。默認爲 false。 -
enumerable
(可枚舉)當且僅當該屬性的enumerable
爲true
時,該屬性才能夠出現在對象的枚舉屬性中。默認爲 false。 -
value
該屬性對應的值。可以是任何有效的 JavaScript 值(數值,對象,函數等)。默認爲undefined。 -
writable
(可寫)當且僅當該屬性的writable
爲true
時,value
才能被賦值運算符改變。默認爲 false。 -
get
一個給屬性提供 getter 的方法,如果沒有 getter 則爲undefined
。當訪問該屬性時,該方法會被執行,方法執行時沒有參數傳入,但是會傳入this
對象(由於繼承關係,這裏的this
並不一定是定義該屬性的對象)。默認爲 undefined。 -
set
一個給屬性提供 setter 的方法,如果沒有 setter 則爲undefined
。當屬性值修改時,觸發執行該方法。該方法將接受唯一參數,即該屬性新的參數值。默認爲 undefined。
描述符可同時具有的鍵值
注意: 如果一個描述符不具有value,writable,get 和 set 任意一個關鍵字,那麼它將被認爲是一個數據描述符。如果一個描述符同時有(value或writable)和(get或set)關鍵字,將會產生一個異常。
實例說明:
/************* 數據描述符 *************/
var o = {};
Object.defineProperty(o, "a", {
value : 37,
writable : true, //可寫,確定屬性值是否可以重新分配。
enumerable : true, //可枚舉,確定是否可以在 for...in 循環和 Object.keys() 中被枚舉
configurable : true //可配置,確定對象的屬性是否可以被刪除,以及除value和writable特性外的其他特性是否可以被修改。
});
//console.log(o.a); //=> 37
/************* 存取描述符 *************/
var bValue;
Object.defineProperty(o, "b", {
get : function(){
return bValue;
},
set : function(newValue){
bValue = newValue;
},
enumerable : true,
configurable : true
});
/* 設置一個初始化值,調用set */
o.b = 38;
/* 獲取屬性屬性值,調用get */
console.log(o.b); //=> 38
/* 重置屬性值,調用set */
o.b = 40;
console.log(bValue); //=> 40
上述代碼,對 vue 響應式原理有很好的理解。可以這麼說 vue 響應式原理就是基於此。