前端点滴(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 响应式原理就是基于此。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值