GithubHelp home page GithubHelp logo

blog's People

Contributors

itkasumy avatar

blog's Issues

ES6系列之WeakMap

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上的区别主要是两个:

  • 一是没有遍历操作(即没有key()、values()和entries()方法),也没有size属性;
  • 二是无法清空,即不支持clear方法。这与WeakMap的键不被计入引用、被垃圾回收机制忽略有关。

因此,WeakMap只有四个方法可用:get()、set()、has()、delete()。

ES6系列之Iterator接口与Generator函数

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命令给出每一步的返回值即可。

ES6系列之Promise实例添加状态改变时的回调函数

Promise实例具有then方法,也就是说,then方法是定义在原型对象,作用是为Promise实例添加状态改变时的回调函数。

then方法两个参数:

  • Resolved状态的回调函数;
  • Rejected状态的回调函数(可选)。

then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法。

getJSON("/posts.json").then(function(json) {
  return json.post;
}).then(function(post) {
  // ...
});

上面的代码使用then方法,依次指定了两个回调函数。第一个回调函数完成以后,会将返回结果作为参数,传入第二个回调函数。

ES6系列之属性名表达式

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

ES6系列之默认参数

现在可以在定义函数的时候指定参数的默认值了,而不用像以前那样通过逻辑或操作符来达到目的了。

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 汇智网

ES6系列之比较两个值是否严格相等

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

ES6系列之模板字符串

模板字符串提供了几个有意思的特性

模板字符中,支持字符串插值:

let first = 'hubwiz';
let last = '汇智网';
document.write(`Hello ${first} ${last}!`);
// Hello hubwiz 汇智网!

模板字符串可以包含多行:

let multiLine = `
    This is
    a string
    with multiple
    lines`;

ES6系列之repeat()原字符串重复

repeat()返回一个新字符串,表示将原字符串重复n次。

var str = "x";
str.repeat(3) // "xxx"
 
var str1 = "hello";
str1.repeat(2) // "hellohello"

02-双向数据绑定原理 -- vue

index.html

<!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>

binding.js

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
})

ES6系列之箭头函数

箭头函数是使用=>语法的函数简写形式。这在语法上与 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对象的指向是可变的,但是在箭头函数中,它是固定的。

ES6系列之Set实例的属性

Set结构的实例有以下属性。

  • Set.prototype.constructor:构造函数,默认就是Set函数。
  • Set.prototype.size:返回Set实例的成员总数。
let s = new Set();
s.add(1).add(2).add(2);
// 注意2被加入了两次
 
s.size // 2

ES6系列之是否包含字符串三种新方法

传统上,JavaScript只有 indexOf 方法,可以用来确定一个字符串是否包含在另一个字符串中。ES6又提供了三种新方法。

  • includes():返回布尔值,表示是否找到了参数字符串。
  • startsWith():返回布尔值,表示参数字符串是否在源字符串的头部。
  • endsWith():返回布尔值,表示参数字符串是否在源字符串的尾部。
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个位置直到字符串结束。

ES6系列之将两类对象转为真正的数组

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;
}

ES6系列之找出第一个符合条件的数组成员和位置

数组实例的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方法做到。

ES6系列之Promise.all()方法

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决定,分成两种情况。

  • 只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
  • 只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。

下面是一个具体的例子。

// 生成一个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){
  // ...
});

ES6系列之原生具备Iterator接口的数据结构

字符串是一个类似数组的对象,也原生具有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。

ES6系列之遍历方法

Map原生提供三个遍历器。

  • keys():返回键名的遍历器。
  • values():返回键值的遍历器。
  • entries():返回所有成员的遍历器。

使用示例:

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);
};

ES6系列之Promise的含义

所谓Promise,就是一个对象,用来传递异步操作的消息。

Promise对象有以下两个特点:

  • 对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:Pending(进行中)、Resolved(已完成,又称Fulfilled)和Rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。
  • 一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从Pending变为Resolved和从Pending变为Rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。就算改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。

有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise对象提供统一的接口,使得控制异步操作更加容易。

Promise也有一些缺点:

  • 首先,无法取消Promise,一旦新建它就会立即执行,无法中途取消。
  • 其次,如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。
  • 第三,当处于Pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

ES6系列之proto属性

proto属性,用来读取或设置当前对象的prototype对象。该属性一度被正式写入ES6草案,但后来又被移除。目前,所有浏览器(包括IE11)都部署了这个属性。

// es6的写法
 
var obj = {
  __proto__: someOtherObj,
  method: function() { ... }
}
 
// es5的写法
 
var obj = Object.create(someOtherObj);
obj.method = function() { ... }

ES6系列之将值转换为数组

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()才会返回由参数组成的新数组。

ES6系列之Map结构的目的和基本用法

Map 是一个“超对象”,其 key 除了可以是 String 类型之外,还可以为其他类型(如:对象)

var m = new Map();
 
o = {p: "Hello World"};
 
m.set(o, "content")
 
document.write(m.get(o))
// "content"

他的方法和 Set 差不多:

  • size:返回成员总数。
  • set(key, value):设置一个键值对。
  • get(key):读取一个键。
  • has(key):返回一个布尔值,表示某个键是否在Map数据结构中。
  • delete(key):删除某个键。
  • clear():清除所有成员。
  • keys():返回键名的遍历器。
  • values():返回键值的遍历器。
  • entries():返回所有成员的遍历器。

ES6系列之Iterator(遍历器)的概念

遍历器(Iterator)就是统一的接口机制,来处理所有不同的数据结构。

Iterator的作用有三个:一是为各种数据结构,提供一个统一的、简便的访问接口;二是使得数据结构的成员能够按某种次序排列;三是ES6创造了一种新的遍历命令for...of循环,Iterator接口主要供for...of消费。

Iterator的遍历过程是这样的。

  • 创建一个指针,指向当前数据结构的起始位置。也就是说,遍历器的返回值是一个指针对象。
  • 第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。
  • 第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。
  • 调用指针对象的next方法,直到它指向数据结构的结束位置。

每一次调用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'

ES6系列之扩展运算符

扩展运算符(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 张三,李四,王五 

ES6系列之遍历器的return(),throw()

遍历器返回的指针对象除了具有next方法,还可以具有return方法和throw方法。其中,next方法是必须部署的,return方法和throw方法是否部署是可选的。

return方法的使用场合是,如果for...of循环提前退出(通常是因为出错,或者有break语句或continue语句),就会调用return方法。如果一个对象在完成遍历前,需要清理或释放资源,就可以部署return方法。

throw方法主要是配合Generator函数使用,一般的遍历器用不到这个方法。

ES6系列之for...of循环

迭代器对象允许像 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);
}

ES6系列之Object.assign()

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}

ES6系列之标签模板

标签模板

var a = 5;
var b = 10;
 
tag`Hello ${ a + b } world ${ a * b }`;

上面代码中,模板字符串前面有一个标识名tag,它是一个函数。整个表达式的返回值,就是tag函数处理模板字符串后的返回值

tag函数所有参数的实际值如下。

  • 第一个参数:['Hello ', ' world ']
  • 第二个参数: 15
  • 第三个参数:50

也就是说,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"

ES6系列之调用默认Iterator接口的场合

有一些场合会默认调用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接口,可以查阅相关章节。

  • yield*
  • Array.from()
  • Map(), Set(), WeakMap(), WeakSet()
  • Promise.all(), Promise.race()

ES6系列之Set实例的方法

Set实例的方法分为两大类:操作方法(用于操作数据)和遍历方法(用于遍历成员)。下面先介绍四个操作方法。

  • add(value):添加某个值,返回Set结构本身。
  • delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
  • has(value):返回一个布尔值,表示该值是否为Set的成员。
  • clear():清除所有成员,没有返回值。

上面这些属性和方法的实例如下:

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);

ES6系列之基本用法

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"
});
  • resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从Pending变为Resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
  • reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从Pending变为Rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

ES6系列之String.raw()

模板字符串可以是原始的:

ES6还为原生的String对象,提供了一个raw方法。

若使用String.raw 作为模板字符串的前缀,则模板字符串可以是原始(raw)的。反斜线也不再是特殊字符,\n 也不会被解释成换行符

  let raw = String.raw`Not a newline: \n`;
  document.write(raw === 'Not a newline: \\n'); // true

ES6系列之数组实例的entries(),keys()和values()

ES6提供三个新的方法:

  • entries()
  • keys()
  • values()

用于遍历数组。它们都返回一个遍历器,可以用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"

ES6系列之指定发生错误时的回调函数

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捕获。

ES6系列之fill()填充数组

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系列之属性的简洁表示法

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}

ES6系列之Math 对数方法

Math.expm1(x):返回ex - 1。

Math.expm1(-1); // -0.6321205588285577
Math.expm1(0);  // 0
Math.expm1(1);  // 1.718281828459045

Math.log1p(x):返回1 + x的自然对数。如果x小于-1,返回NaN。

Math.log1p(1);  // 0.6931471805599453
Math.log1p(0);  // 0
Math.log1p(-1); // -Infinity
Math.log1p(-2); // NaN

Math.log10(x):返回以10为底的x的对数。如果x小于0,则返回NaN。

Math.log10(2);      // 0.3010299956639812
Math.log10(1);      // 0
Math.log10(0);      // -Infinity
Math.log10(-2);     // NaN
Math.log10(100000); // 5

Math.log2(x):返回以2为底的x的对数。如果x小于0,则返回NaN。

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系列之数据结构的默认Iterator接口

在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属性上面。所以,调用这个属性,就得到遍历器。

ES6系列之WeakSet

WeakSet和Set一样都不存储重复的元素,但有一些不同点 。

WeakSet的成员只能是对象,而不能是其他类型的值。

var ws = new WeakSet();
ws.add(1)
// TypeError: Invalid value used in weak set

WeakSet结构有以下三个方法。

  • WeakSet.prototype.add(value):向WeakSet实例添加一个新成员。
  • WeakSet.prototype.delete(value):清除WeakSet实例的指定成员。
  • WeakSet.prototype.has(value):返回一个布尔值,表示某个值是否在
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

ES6系列之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函数的参数。

ES6系列之尾调用优化

什么是尾调用?

尾调用的概念非常简单,一句话就能说清楚,就是指某个函数的最后一步是调用另一个函数。

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),即只保留内层函数的调用帧,这样可以节省内存。

ES6系列之Math对象

Math对象新增的方法,都是静态方法,只能在Math对象上调用。

Math.trunc():去除一个数的小数部分,返回整数部分。

Math.trunc(4.1) // 4
Math.trunc(-4.1) // -4

注意:对于空值和无法截取整数的值,返回NaN。

Math.sign():判断一个数到底是正数、负数、还是零。

返回五种值:参数为正数,返回+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:计算一个数的立方根。

Math.cbrt(-1); // -1
Math.cbrt(0);  // 0
Math.cbrt(2);  // 1.2599210498948732

Math.fround:返回一个数的单精度浮点数形式。

Math.fround(0);     // 0
Math.fround(1.337); // 1.3370000123977661
Math.fround(NaN);   // NaN

Math.hypot:返回所有参数的平方和的平方根。

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

ES6系列之Set基本用法

数据结构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

上面代码表示,由于两个空对象不是精确相等,所以它们被视为两个值。

ES6系列之值是否整数

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系列之值是否无穷,NaN

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```

ES6系列之实例的属性和操作方法

Map结构的实例有以下属性和操作方法。

  • size:返回成员总数。
  • set(key, value):设置key所对应的键值,然后返回整个Map结构。如果key已经有值,则键值会被更新,否则就新生成该键。
  • get(key):读取key对应的键值,如果找不到key,返回undefined。
  • has(key):返回一个布尔值,表示某个键是否在Map数据结构中。
  • delete(key):删除某个键,返回true。如果删除失败,返回false。
  • clear():清除所有成员,没有返回值。

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

ES6系列之Proxy内置代理

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)。

ES6系列之函数绑定

函数绑定运算符是并排的两个双引号(::),双引号左边是一个对象,右边是一个函数。该运算符会自动将左边的对象,作为上下文环境(即this对象),绑定到右边的函数上面。

let log = ::console.log;
// 等同于
var log = console.log.bind(console);
 
foo::bar;
// 等同于
bar.call(foo);
 
foo::bar(...arguments);
i// 等同于
bar.apply(foo, arguments);

01-双向数据绑定原理 -- ng

index.html

<!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>

digest.js

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类型

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)'

ES6系列之Set遍历操作

Set结构的实例有四个遍历方法,可以用于遍历成员。

  • keys():返回一个键名的遍历器
  • values():返回一个键值的遍历器
  • entries():返回一个键值对的遍历器
  • forEach():使用回调函数遍历每个成员

由于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);
})

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.