前端點滴(ES6+)(一)

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聲明的變量,塊級變量,聲明的對象具有塊級作用域。

四大特性:

  1. 塊級作用域:{}下起作用。

  2. 同級作用域下,只能存在一個(let聲明的變量不能重複聲明)。

  3. 不能變量提升。

  4. 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聲明的變量稱爲常量,即不可改變其值的量。

四大特性:

  1. 不能修改常量值。

  2. 不能重複聲明。

  3. 聲明後必須賦值,因爲const聲明的常量不能被修改。

  4. 常量不可改的是其地址,聲明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

注意點:

  1. 箭頭函數沒有自己的this,函數內部寫的this,指的是外層代碼塊中的this,所以在箭頭函數內部不要使用this。

  2. this指向定義時的對象,也就是說箭頭函數一旦定義完成,它的指向是固定的,沒法改變,它的指向是定義時所在的作用域,而不是執行時的作用域。

  3. 箭頭函數不能用作構造函數。

  4. 箭頭函數內部不存在arguments,箭頭函數體中arguments其實指向的是外層函數的arguments。

var obj1 = {
    name:'yaodao',
    fn:()=>{
        console.log(this.name);
        console.log(this);     
    },
}
obj1.fn();

結果:
在這裏插入圖片描述

個人總結:

1.箭頭函數的this指向是定義(聲明)時就綁定的,和執行無關

2.箭頭函數沒有自己的this,繼承了當前所在環境執行時的this指向

遇到箭頭函數解題:

  1. 看當前箭頭函數定義的環境是什麼?( 小技巧:找上一個{}

  2. 遇到箭頭函數的執行或調用,忽略,不看,對箭頭函數this指向沒有影響

  3. 判斷當前環境執行時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的默認對象表示方式{}可以視爲其他語言中的MapDictionary的數據結構,即一組鍵值對。

但是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需要一個二維數組,或者直接初始化一個空MapMap具有以下方法:

  • 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

SetMap類似,也是一組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(可枚舉)當且僅當該屬性的enumerabletrue時,該屬性才能夠出現在對象的枚舉屬性中。默認爲 false

  • value該屬性對應的值。可以是任何有效的 JavaScript 值(數值,對象,函數等)。默認爲undefined

  • writable(可寫)當且僅當該屬性的writabletrue時,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 響應式原理就是基於此。

發佈了37 篇原創文章 · 獲贊 6 · 訪問量 2219
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章