itkasumy / blog Goto Github PK
View Code? Open in Web Editor NEW宗主的博客
License: MIT License
宗主的博客
License: MIT License
WeakMap结构与Map结构基本类似,唯一的区别是它只接受对象作为键名(null除外),不接受原始类型的值作为键名,而且键名所指向的对象,不计入垃圾回收机制。set()和get()分别用来添加数据和获取数据:
var map = new WeakMap(),
element = document.querySelector(".element");
map.set(element, "Original");
// 下面就可以使用了
var value = map.get(element);
document.write(value); // "Original"
WeakMap与Map在API上的区别主要是两个:
因此,WeakMap只有四个方法可用:get()、set()、has()、delete()。
Symbol.iterator方法的最简单实现,还是使用下一章要介绍的Generator函数。
var myIterable = {};
myIterable[Symbol.iterator] = function* () {
yield 1;
yield 2;
yield 3;
};
[...myIterable] // [1, 2, 3]
// 或者采用下面的简洁写法
let obj = {
* [Symbol.iterator]() {
yield 'hello';
yield 'world';
}
};
for (let x of obj) {
document.write(x);
}
// hello
// world
上面代码中,Symbol.iterator方法几乎不用部署任何代码,只要用yield命令给出每一步的返回值即可。
Promise实例具有then方法,也就是说,then方法是定义在原型对象,作用是为Promise实例添加状态改变时的回调函数。
then方法两个参数:
then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法。
getJSON("/posts.json").then(function(json) {
return json.post;
}).then(function(post) {
// ...
});
上面的代码使用then方法,依次指定了两个回调函数。第一个回调函数完成以后,会将返回结果作为参数,传入第二个回调函数。
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
现在可以在定义函数的时候指定参数的默认值了,而不用像以前那样通过逻辑或操作符来达到目的了。
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 汇智网
Object.is()用来比较两个值是否严格相等。它与严格比较运算符(===)的行为基本一致,不同之处只有两个:一是+0不等于-0,二是NaN等于自身。
+0 === -0 //true
NaN === NaN // false
Object.is(+0, -0) // false
Object.is(NaN, NaN) // true
Object.is(3, 3.0) // true
模板字符串提供了几个有意思的特性
let first = 'hubwiz';
let last = '汇智网';
document.write(`Hello ${first} ${last}!`);
// Hello hubwiz 汇智网!
let multiLine = `
This is
a string
with multiple
lines`;
repeat()返回一个新字符串,表示将原字符串重复n次。
var str = "x";
str.repeat(3) // "xxx"
var str1 = "hello";
str1.repeat(2) // "hellohello"
<!DOCTYPE html>
<html>
<head>
<title>vue双向数据绑定的原理</title>
<meta charset="utf-8">
</head>
<body>
<p> 此例实现的效果是:随文本框输入文字的变化,span 中会同步显示相同的文字内容;在js或控制台显式的修改 obj.hello 的值,视图会相应更新。这样就实现了 model => view 以及 view => model 的双向绑定。</p>
<input type="text" id="txt" />
<span id="sp"></span>
</body>
<script src="binding.js"></script>
</html>
var txt = document.getElementById('txt'),
sp = document.getElementById('sp'),
obj = {}
// 给对象obj添加msg属性,并设置setter访问器
Object.defineProperty(obj, 'msg', {
// 设置 obj.msg 当obj.msg反生改变时set方法将会被调用
set: function (newVal) {
// 当obj.msg被赋值时 同时设置给 input/span
txt.value = newVal
sp.innerText = newVal
}
})
// 监听文本框的改变 当文本框输入内容时 改变obj.msg
txt.addEventListener('keyup', function (event) {
obj.msg = event.target.value
})
箭头函数是使用=>语法的函数简写形式。这在语法上与 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对象的指向是可变的,但是在箭头函数中,它是固定的。
Set结构的实例有以下属性。
let s = new Set();
s.add(1).add(2).add(2);
// 注意2被加入了两次
s.size // 2
传统上,JavaScript只有 indexOf 方法,可以用来确定一个字符串是否包含在另一个字符串中。ES6又提供了三种新方法。
var str = "Hello world!";
str.startsWith("Hello") // true
str.endsWith("!") // true
str.includes("o") // true
这三个方法都支持第二个参数,表示开始搜索的位置。
var str = "Hello world!";
str.startsWith("world", 6) // true
str.endsWith("Hello", 5) // true
str.includes("Hello", 6) // false
上面代码表示,使用第二个参数n时,endsWith 的行为与其他两个方法有所不同。它针对前n个字符,而其他两个方法针对从第n个位置直到字符串结束。
Array.from方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括ES6新增的数据结构Set和Map)。
一个转换类数组对象到数组的一个示例:
let list = document.querySelectorAll('ul.fancy li');
Array.from(list).forEach(function (li) {
document.write(li);
});
上面代码中,querySelectorAll方法返回的是一个类似数组的对象,只有将这个对象转为真正的数组,才能使用forEach方法。
任何有length属性的对象,都可以通过Array.from方法转为数组。
let array = Array.from({ 0: "a", 1: "b", 2: "c", length: 3 });
document.write(array); // [ "a", "b" , "c" ]
Array.from()还可以接受第二个参数,作用类似于数组的map方法,用来对每个元素进行处理。
let array = [0,1,2,3,4];
let arrNew = Array.from(array, x => x * x);
console.log(arrNew);
// 等同于
let arrNew = Array.from(array).map(x => x * x);
下面的例子将数组中布尔值为false的成员转为0。
Array.from([1, , 2, , 3], (n) => n || 0)
// [1, 0, 2, 0, 3]
Array.from()的一个应用是,将字符串转为数组,然后返回字符串的长度。这样可以避免JavaScript将大于\uFFFF的Unicode字符,算作两个字符的bug。
function countSymbols(string) {
return Array.from(string).length;
}
数组实例的find方法,用于找出第一个符合条件的数组成员。它的参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为true的成员,然后返回该成员。如果没有符合条件的成员,则返回undefined。
let array = [1, 4, -5, 10].find((n) => n < 0);
document.write("array:", array);
上面代码找出数组中第一个小于0的成员。
let array = [1, 5, 10, 15].find(function(value, index, arr) {
return value > 9;
})
document.write(array); // 10
上面代码中,find方法的回调函数可以接受三个参数,依次为当前的值、当前的位置和原数组。
数组实例的findIndex方法,用法与find方法非常类似,返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1。
let index = [1, 5, 10, 15].findIndex(function(value, index, arr) {
return value > 9;
})
document.write(index); // 2
这两个方法都可以接受第二个参数,用来绑定回调函数的this对象。
另外,这两个方法都可以发现NaN,弥补了数组的IndexOf方法的不足。
[NaN].indexOf(NaN)
// -1
[NaN].findIndex(y => Object.is(NaN, y))
// 0
上面代码中,indexOf方法无法识别数组的NaN成员,但是findIndex方法可以借助Object.is方法做到。
Promise.all方法用于将多个Promise实例,包装成一个新的Promise实例。
var p = Promise.all([p1,p2,p3]);
上面代码中,Promise.all方法接受一个数组作为参数,p1、p2、p3都是Promise对象的实例。(Promise.all方法的参数不一定是数组,但是必须具有iterator接口,且返回的每个成员都是Promise实例。)
p的状态由p1、p2、p3决定,分成两种情况。
下面是一个具体的例子。
// 生成一个Promise对象的数组
var promises = [2, 3, 5, 7, 11, 13].map(function(id){
return getJSON("/post/" + id + ".json");
});
Promise.all(promises).then(function(posts) {
// ...
}).catch(function(reason){
// ...
});
字符串是一个类似数组的对象,也原生具有Iterator接口。
var someString = "hi";
typeof someString[Symbol.iterator]
// "function"
var iterator = someString[Symbol.iterator]();
iterator.next() // { value: "h", done: false }
iterator.next() // { value: "i", done: false }
iterator.next() // { value: undefined, done: true }
上面代码中,调用Symbol.iterator方法返回一个遍历器,在这个遍历器上可以调用next方法,实现对于字符串的遍历。
可以覆盖原生的Symbol.iterator方法,达到修改遍历器行为的目的。
var str = new String("hi");
[...str] // ["h", "i"]
str[Symbol.iterator] = function() {
return {
next: function() {
if (this._first) {
this._first = false;
return { value: "bye", done: false };
} else {
return { done: true };
}
},
_first: true
};
};
[...str] // ["bye"]
str // "hi"
上面代码中,字符串str的Symbol.iterator方法被修改了,所以扩展运算符(...)返回的值变成了bye,而字符串本身还是hi。
Map原生提供三个遍历器。
使用示例:
let map = new Map([
['F', 'no'],
['T', 'yes'],
]);
for (let key of map.keys()) {
document.write(key);
}
// "F"
// "T"
for (let value of map.values()) {
document.write(value);
}
// "no"
// "yes"
for (let item of map.entries()) {
document.write(item[0], item[1]);
}
// "F" "no"
// "T" "yes"
// 或者
for (let [key, value] of map.entries()) {
document.write(key, value);
}
// 等同于使用map.entries()
for (let [key, value] of map) {
document.write(key, value);
}
Map结构转为数组结构,比较快速的方法是结合使用扩展运算符(...)。
let map = new Map([
[1, 'one'],
[2, 'two'],
[3, 'three'],
]);
[...map.keys()]
// [1, 2, 3]
[...map.values()]
// ['one', 'two', 'three']
[...map.entries()]
// [[1,'one'], [2, 'two'], [3, 'three']]
[...map]
// [[1,'one'], [2, 'two'], [3, 'three']]
此外,Map还有一个forEach方法,与数组的forEach方法类似,也可以实现遍历。
map.forEach(function(value, key, map)) {
document.write("Key: %s, Value: %s", key, value);
};
所谓Promise,就是一个对象,用来传递异步操作的消息。
Promise对象有以下两个特点:
有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise对象提供统一的接口,使得控制异步操作更加容易。
Promise也有一些缺点:
proto属性,用来读取或设置当前对象的prototype对象。该属性一度被正式写入ES6草案,但后来又被移除。目前,所有浏览器(包括IE11)都部署了这个属性。
// es6的写法
var obj = {
__proto__: someOtherObj,
method: function() { ... }
}
// es5的写法
var obj = Object.create(someOtherObj);
obj.method = function() { ... }
Array.of方法用于将一组值,转换为数组。
Array.of(3, 11, 8) // [3,11,8]
Array.of(3) // [3]
Array.of(3).length // 1
这个方法的主要目的,是弥补数组构造函数Array()的不足。因为参数个数的不同,会导致Array()的行为有差异。
Array() // []
Array(3) // [undefined, undefined, undefined]
Array(3,11,8) // [3, 11, 8]
上面代码说明,只有当参数个数不少于2个,Array()才会返回由参数组成的新数组。
Map 是一个“超对象”,其 key 除了可以是 String 类型之外,还可以为其他类型(如:对象)
var m = new Map();
o = {p: "Hello World"};
m.set(o, "content")
document.write(m.get(o))
// "content"
他的方法和 Set 差不多:
遍历器(Iterator)就是统一的接口机制,来处理所有不同的数据结构。
Iterator的作用有三个:一是为各种数据结构,提供一个统一的、简便的访问接口;二是使得数据结构的成员能够按某种次序排列;三是ES6创造了一种新的遍历命令for...of循环,Iterator接口主要供for...of消费。
Iterator的遍历过程是这样的。
每一次调用next方法,都会返回当前成员的信息,具体来说,就是返回一个包含value和done两个属性的对象。其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束。
function idMaker(){
var index = 0;
return {
next: function(){
return {value: index++, done: false};
}
}
}
var it = idMaker();
it.next().value // '0'
it.next().value // '1'
it.next().value // '2'
扩展运算符(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 张三,李四,王五
遍历器返回的指针对象除了具有next方法,还可以具有return方法和throw方法。其中,next方法是必须部署的,return方法和throw方法是否部署是可选的。
return方法的使用场合是,如果for...of循环提前退出(通常是因为出错,或者有break语句或continue语句),就会调用return方法。如果一个对象在完成遍历前,需要清理或释放资源,就可以部署return方法。
throw方法主要是配合Generator函数使用,一般的遍历器用不到这个方法。
迭代器对象允许像 CLI IEnumerable 或者 Java Iterable 一样自定义迭代器。将for..in转换为自定义的基于迭代器的形如for..of的迭代,不需要实现一个数组,支持像 LINQ 一样的惰性设计模式。
let fibonacci = {
[Symbol.iterator]() {
let pre = 0, cur = 1;
return {
next() {
[pre, cur] = [cur, pre + cur];
return { done: false, value: cur }
}
}
}
}
for (var n of fibonacci) {
// truncate the sequence at 1000
if (n > 1000)
break;
document.write(n);
}
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}
标签模板
var a = 5;
var b = 10;
tag`Hello ${ a + b } world ${ a * b }`;
上面代码中,模板字符串前面有一个标识名tag,它是一个函数。整个表达式的返回值,就是tag函数处理模板字符串后的返回值
tag函数所有参数的实际值如下。
也就是说,tag函数实际上以下面的形式调用。
tag(['Hello ', ' world '], 15, 50)
下面是tag函数的一种写法,以及运行结果。
var a = 5;
var b = 10;
function tag(s, v1, v2) {
document.write(s[0]);
document.write(s[1]);
document.write(v1);
document.write(v2);
return "OK";
}
tag`Hello ${ a + b } world ${ a * b}`;
// "Hello "
// " world "
// 15
// 50
// "OK"
有一些场合会默认调用iterator接口(即Symbol.iterator方法),除了下文会介绍的for...of循环,还有几个别的场合。
有一些场合会默认调用iterator接口(即Symbol.iterator方法),除了下文会介绍的for...of循环,还有几个别的场合。
对数组和Set结构进行解构赋值时,会默认调用iterator接口。
let set = new Set().add('a').add('b').add('c');
let [x,y] = set;
// x='a'; y='b'
let [first, ...rest] = set;
// first='a'; rest=['b','c'];
扩展运算符(...)也会调用默认的iterator接口。
// 例一
var str = 'hello';
[...str] // ['h','e','l','l','o']
// 例二
let arr = ['b', 'c'];
['a', ...arr, 'd']
// ['a', 'b', 'c', 'd']
以下场合也会用到默认的iterator接口,可以查阅相关章节。
Set实例的方法分为两大类:操作方法(用于操作数据)和遍历方法(用于遍历成员)。下面先介绍四个操作方法。
上面这些属性和方法的实例如下:
let s = new Set();
s.add(1).add(2).add(2);
s.has(1) // true
s.has(2) // true
s.has(3) // false
s.delete(2);
s.has(2) // false
Array.from方法可以将Set结构转为数组:
var items = new Set([1, 2, 3, 4, 5]);
var array = Array.from(items);
Promise对象是一个构造函数,用来生成Promise实例
//创建promise
var promise = new Promise(function(resolve, reject) {
// 进行一些异步或耗时操作
if ( /*如果成功 */ ) {
resolve("Stuff worked!");
} else {
reject(Error("It broke"));
}
});
//绑定处理程序
promise.then(function(result) {
//promise成功的话会执行这里
document.write(result); // "Stuff worked!"
}, function(err) {
//promise失败会执行这里
document.write(err); // Error: "It broke"
});
ES6还为原生的String对象,提供了一个raw方法。
若使用String.raw 作为模板字符串的前缀,则模板字符串可以是原始(raw)的。反斜线也不再是特殊字符,\n 也不会被解释成换行符
let raw = String.raw`Not a newline: \n`;
document.write(raw === 'Not a newline: \\n'); // true
ES6提供三个新的方法:
用于遍历数组。它们都返回一个遍历器,可以用for...of循环进行遍历,唯一的区别是keys()是对键名的遍历、values()是对键值的遍历,entries()是对键值对的遍历。
for (let index of ['a', 'b'].keys()) {
document.write(index);
}
// 0
// 1
for (let elem of ['a', 'b'].values()) {
document.write(elem);
}
// 'a'
// 'b'
for (let [index, elem] of ['a', 'b'].entries()) {
document.write(index, elem);
}
// 0 "a"
// 1 "b"
Promise.prototype.catch方法是.then(null, rejection)的别名,用于指定发生错误时的回调函数。
getJSON("/posts.json").then(function(posts) {
// ...
}).catch(function(error) {
// 处理前一个回调函数运行时发生的错误
document.write('发生错误!', error);
});
getJSON方法返回一个Promise对象,如果该对象状态变为Resolved,则会调用then方法指定的回调函数;如果异步操作抛出错误,状态就会变为Rejected,就会调用catch方法指定的回调函数,处理这个错误。
var promise = new Promise(function(resolve, reject) {
throw new Error('test')
});
promise.catch(function(error) { document.write(error) });
// Error: test
上面代码中,Promise抛出一个错误,就被catch方法指定的回调函数捕获。
Promise对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获。
getJSON("/post/1.json").then(function(post) {
return getJSON(post.commentURL);
}).then(function(comments) {
// some code
}).catch(function(error) {
// 处理前面三个Promise产生的错误
});
上面代码中,一共有三个Promise对象:一个由getJSON产生,两个由then产生。它们之中任何一个抛出的错误,都会被最后一个catch捕获。
fill()使用给定值,填充一个数组。
let arr = ['a', 'b', 'c'].fill(7)
document.write(arr); // [7, 7, 7]
let newArr = new Array(3).fill(7)
document.write(newArr); // [7, 7, 7]
上面代码表明,fill方法用于空数组的初始化非常方便。数组中已有的元素,会被全部抹去。
fill()还可以接受第二个和第三个参数,用于指定填充的起始位置和结束位置。
let newArr = ['a', 'b', 'c'].fill(7, 1, 2)
document.write(newArr); // ['a', 7, 'c']
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}
Math.expm1(-1); // -0.6321205588285577
Math.expm1(0); // 0
Math.expm1(1); // 1.718281828459045
Math.log1p(1); // 0.6931471805599453
Math.log1p(0); // 0
Math.log1p(-1); // -Infinity
Math.log1p(-2); // NaN
Math.log10(2); // 0.3010299956639812
Math.log10(1); // 0
Math.log10(0); // -Infinity
Math.log10(-2); // NaN
Math.log10(100000); // 5
Math.log2(3); // 1.584962500721156
Math.log2(2); // 1
Math.log2(1); // 0
Math.log2(0); // -Infinity
Math.log2(-2); // NaN
Math.log2(1024); // 10
Math.sinh(x) 返回x的双曲正弦(hyperbolic sine)
Math.cosh(x) 返回x的双曲余弦(hyperbolic cosine)
Math.tanh(x) 返回x的双曲正切(hyperbolic tangent)
Math.asinh(x) 返回x的反双曲正弦(inverse hyperbolic sine)
Math.acosh(x) 返回x的反双曲余弦(inverse hyperbolic cosine)
Math.atanh(x) 返回x的反双曲正切(inverse hyperbolic tangent)
在ES6中,可迭代数据结构(比如数组)都必须实现一个名为Symbol.iterator的方法,该方法返回一个该结构元素的迭代器。注意,Symbol.iterator是一个Symbol,Symbol是ES6新加入的原始值类型。
let arr = ['a', 'b', 'c'];
let iter = arr[Symbol.iterator]();
iter.next() // { value: 'a', done: false }
iter.next() // { value: 'b', done: false }
iter.next() // { value: 'c', done: false }
iter.next() // { value: undefined, done: true }
上面代码中,变量arr是一个数组,原生就具有遍历器接口,部署在arr的Symbol.iterator属性上面。所以,调用这个属性,就得到遍历器。
WeakSet和Set一样都不存储重复的元素,但有一些不同点 。
WeakSet的成员只能是对象,而不能是其他类型的值。
var ws = new WeakSet();
ws.add(1)
// TypeError: Invalid value used in weak set
WeakSet结构有以下三个方法。
var ws = new WeakSet();
var obj = {};
var foo = {};
ws.add(window);
ws.add(obj);
ws.has(window); // true
ws.has(foo); // false
ws.delete(window);
ws.has(window); // false
WeakSet没有size属性,没有办法遍历它的成员。
ws.size // undefined
ws.forEach // undefined
ws.forEach(function(item){ document.write('WeakSet has ' + item)})
// TypeError: undefined is not a function
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函数的参数。
尾调用的概念非常简单,一句话就能说清楚,就是指某个函数的最后一步是调用另一个函数。
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),即只保留内层函数的调用帧,这样可以节省内存。
Math对象新增的方法,都是静态方法,只能在Math对象上调用。
Math.trunc(4.1) // 4
Math.trunc(-4.1) // -4
注意:对于空值和无法截取整数的值,返回NaN。
返回五种值:参数为正数,返回+1;参数为负数,返回-1;参数为0,返回0;参数为-0,返回-0;其他值,返回NaN。
Math.sign(-5) // -1
Math.sign(5) // +1
Math.sign(0) // +0
Math.sign(-0) // -0
Math.sign('hubwiz'); // NaN
Math.cbrt(-1); // -1
Math.cbrt(0); // 0
Math.cbrt(2); // 1.2599210498948732
Math.fround(0); // 0
Math.fround(1.337); // 1.3370000123977661
Math.fround(NaN); // NaN
Math.hypot(3, 4); // 5
Math.hypot(3, 4, 5); // 7.0710678118654755
Math.hypot(); // 0
Math.hypot(NaN); // NaN
Math.hypot(3, 4, 'foo'); // NaN
Math.hypot(3, 4, '5'); // 7.0710678118654755
Math.hypot(-3); // 3
如果参数不是数值,Math.hypot方法会将其转为数值。只要有一个参数无法转为数值,就会返回NaN
数据结构Set类似于数组,但是成员的值都是唯一的,没有重复的值。
var s = new Set();
[2,3,5,4,5,2,2].map(x => s.add(x))
for (i of s) {document.write(i)}
// 2 3 5 4
Set函数可以接受一个数组作为参数,用来初始化。
var items = new Set([1,2,3,4,5,5,5,5]);
document.write(items.size); // 5
向Set加入值的时候,不会发生类型转换,所以5和“5”是两个不同的值。
let set = new Set();
set.add({})
set.size // 1
set.add({})
set.size // 2
上面代码表示,由于两个空对象不是精确相等,所以它们被视为两个值。
Number.isInteger()用来判断一个值是否为整数。需要注意的是,在JavaScript内部,整数和浮点数是同样的储存方法,所以3和3.0被视为同一个值。
Number.isInteger(25) // true
Number.isInteger(25.0) // true
Number.isInteger(25.1) // false
Number.isInteger("15") // false
Number.isInteger(true) // false
ES6在Number对象上,新提供了Number.isFinite()和Number.isNaN()两个方法,用来检查Infinite和NaN这两个特殊值。
Number.isFinite()用来检查一个数值是否非无穷(infinity)。
Number.isFinite(15); // true
Number.isFinite(0.8); // true
Number.isFinite(NaN); // false
Number.isFinite(Infinity); // false
Number.isFinite(-Infinity); // false
Number.isFinite("foo"); // false
Number.isFinite("15"); // false
Number.isFinite(true); // false
Number.isNaN()用来检查一个值是否为NaN。
Number.isNaN(NaN); // true
Number.isNaN(15); // false
Number.isNaN("15"); // false
Number.isNaN(true); // false```
Map结构的实例有以下属性和操作方法。
set()方法返回的是Map本身,因此可以采用链式写法。
let map = new Map()
.set(1, 'a')
.set(2, 'b')
.set(3, 'c');
document.write(map);
下面是has()和delete()的例子。
var m = new Map();
m.set("edition", 6) // 键是字符串
m.set(262, "standard") // 键是数值
m.set(undefined, "nah") // 键是undefined
var hello = function() {document.write("hello");}
m.set(hello, "Hello ES6!") // 键是函数
m.has("edition") // true
m.has("years") // false
m.has(262) // true
m.has(undefined) // true
m.has(hello) // true
m.delete(undefined)
m.has(undefined) // false
m.get(hello) // Hello ES6!
m.get("edition") // 6
下面是size属性和clear方法的例子。
let map = new Map();
map.set('foo', true);
map.set('bar', false);
map.size // 2
map.clear()
map.size // 0
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)、proxy.call(object, ...args)、proxy.apply(...)。
construct(target, args, proxy):拦截Proxy实例作为构造函数调用的操作,比如new proxy(...args)。
函数绑定运算符是并排的两个双引号(::),双引号左边是一个对象,右边是一个函数。该运算符会自动将左边的对象,作为上下文环境(即this对象),绑定到右边的函数上面。
let log = ::console.log;
// 等同于
var log = console.log.bind(console);
foo::bar;
// 等同于
bar.call(foo);
foo::bar(...arguments);
i// 等同于
bar.apply(foo, arguments);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<input type="text" />
<input type="text" />
<input type="text" />
</body>
<script src="digest.js"></script>
</html>
const Scope = function () {
this.$$watchers = []
}
Scope.prototype.$watch = function (watchExp, listener) {
this.$$watchers.push({
watchExp: watchExp,
listener: listener || function () { }
})
}
Scope.prototype.$digest = function () {
var dirty
do {
dirty = false
for (let i = 0; i < this.$$watchers.length; i++) {
var newVal = this.$$watchers[i].watchExp()
var oldVal = this.$$watchers[i].last
if (newVal !== oldVal) {
this.$$watchers[i].listener(newVal, oldVal)
dirty = false
this.$$watchers[i].last = newVal
}
}
} while (dirty)
}
const el = document.getElementsByTagName('input')
const $scope = new Scope()
el[0].onkeyup = function () {
$scope.name = el[0].value
$scope.$digest()
}
$scope.$watch(function () {
return $scope.name
}, function () {
el[0].value = $scope.name
el[1].value = $scope.name
el[2].value = $scope.name
})
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)'
Set结构的实例有四个遍历方法,可以用于遍历成员。
由于Set结构没有键名,只有键值(或者说键名和键值是同一个值),所以key方法和value方法的行为完全一致。
let set = new Set(['red', 'green', 'blue']);
for ( let item of set.keys() ){
document.write(item);
}
// red
// green
// blue
for ( let item of set.values() ){
document.write(item);
}
// red
// green
// blue
for ( let item of set.entries() ){
document.write(item);
}
// ["red", "red"]
// ["green", "green"]
// ["blue", "blue"]
set.forEach(function(item){
document.write(item);
})
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.