看阮一峰ES6 笔记

来源:互联网 发布:炒股软件免费版排名 编辑:程序博客网 时间:2024/06/02 21:14
阮一峰 ES6


暂时性死区(定义变量的时候出现)
总之,暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,
但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。


解构(解构赋值对提取JSON对象中的数据,尤其有用)
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构
最简单解构
let [a, b, c] = [1, 2, 3];
上面代码表示,可以从数组中提取值,按照对应位置,对变量赋值。


 解构适用于数组和对象!


解构赋值允许指定默认值。
let [foo = true] = [];
foo // true
let [x, y = 'b'] = ['a']; // x='a', y='b'
let [x, y = 'b'] = ['a', undefined]; // x='a', y='b'


对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;
而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
let { bar, foo } = { foo: "aaa", bar: "bbb" };
foo // "aaa"
bar // "bbb"


最简单两值交换问题解决方法ES6(解构)
let x = 1;
let y = 2;
[x, y] = [y, x];
console.log(x);//2
console.log(y);//1
解构赋值对提取JSON对象中的数据,尤其有用
let jsonData = {
id: 42,
status: "OK",
data: [867, 5309]
};


let { id, status, data } = jsonData;


console.log(id, status, data);
// 42, "OK", [867, 5309]
加载模块时,往往需要指定输入哪些方法。解构赋值使得输入语句非常清晰。


import { SourceMapConsumer, SourceNode } from "source-map";



// ES5的source属性
// 返回正则表达式的正文
/abc/ig.source
// "abc"


// ES6的flags属性
// 返回正则表达式的修饰符
/abc/ig.flags
// 'gi'


它们与传统的全局方法isFinite()和isNaN()的区别在于,传统方法先调用Number()将非数值的值转为数值,
再进行判断,而这两个新方法只对数值有效,Number.isFinite()对于非数值一律返回false, 
Number.isNaN()只有对于NaN才返回true,非NaN一律返回false。
isFinite(25) // true
isFinite("25") // true
Number.isFinite(25) // true
Number.isFinite("25") // false


isNaN(NaN) // true
isNaN("NaN") // true
Number.isNaN(NaN) // true
Number.isNaN("NaN") // false
Number.isNaN(1) // false
Math对象的扩展


Math.trunc(4.1) // 4
Math.trunc(4.9) // 4
Math.trunc(-4.1) // -4
Math.trunc(-4.9) // -4
Math.trunc(-0.1234) // -0
对于非数值,Math.trunc内部使用Number方法将其先转为数值。
Math.trunc('123.456')
// 123
对于空值和无法截取整数的值,返回NaN。
Math.trunc(NaN);      // NaN
Math.trunc('foo');    // NaN
Math.trunc();         // NaN


Math.sign();
Math.sign方法用来判断一个数到底是正数、负数、还是零。
它会返回五种值。
参数为正数,返回+1;
参数为负数,返回-1;
参数为0,返回0;
参数为-0,返回-0;
其他值,返回NaN。


Array新特性
Array.from();
let arrayLike = {
    '0': 'a',
    '1': 'b',
    '2': 'c',
    length: 3
};
let arr1 = [].slice.call(arrayLike);// ['a', 'b', 'c'];
let arr2 = Array.from(arrayLike); //ES6
console.log(arr2);// ['a', 'b', 'c']


Array.of();为了弥补Array的不足


Array(3) // [, , ,]
Array(3, 11, 8) // [3, 11, 8]


Array.of方法用于将一组值,转换为数组。
Array.of(3) // [3]
Array.of(3, 11, 8) // [3,11,8]


数组实例的find()和findIndex()
用于找出第一个符合条件的数组成员。它的参数是一个回调函数,所有数组成员依次执行该回调函数,
直到找出第一个返回值为true的成员,然后返回该成员。如果没有符合条件的成员,则返回undefined。
[1, 4, -5, 10].find((n) => n < 0)
// -5


数组实例的findIndex方法的用法与find方法非常类似,返回第一个符合条件的数组成员的位置,
如果所有成员都不符合条件,则返回-1。
[1, 5, 10, 15].findIndex(function(value, index, arr) {
  return value > 9;
}) // 2


数组实例的fill() 
fill方法使用给定值,填充一个数组。
['a', 'b', 'c'].fill(7)
// [7, 7, 7]
new Array(3).fill(7)
// [7, 7, 7]
上面代码表明,fill方法用于空数组的初始化非常方便。数组中已有的元素,会被全部抹去。
fill方法还可以接受第二个和第三个参数,用于指定填充的起始位置和结束位置。
['a', 'b', 'c'].fill(7, 1, 2)
// ['a', 7, 'c']
遍历数组
数组实例的entries(),keys()和values() 
ES6提供三个新的方法——entries(),keys()和values()——用于遍历数组。它们都返回一个遍历器对象
(详见《Iterator》一章),可以用for...of循环进行遍历,唯一的区别是keys()是对键名的遍历、
values()是对键值的遍历,entries()是对键值对的遍历。


for (let index of ['a', 'b'].keys()) {
  console.log(index);
}
// 0
// 1


for (let elem of ['a', 'b'].values()) {
  console.log(elem);
}
// 'a'
// 'b'


for (let [index, elem] of ['a', 'b'].entries()) {
  console.log(index, elem);
}
// 0 "a"
// 1 "b"




数组实例的includes()
[1, 2, 3].includes(2);     // true
[1, 2, 3].includes(4);     // false
[1, 2, NaN].includes(NaN); // true
该方法的第二个参数表示搜索的起始位置,默认为0。如果第二个参数为负数,则表示倒数的位置,
如果这时它大于数组长度(比如第二个参数为-4,但数组长度为3),则会重置为从0开始。
[1, 2, 3].includes(3, 3);  // false
[1, 2, 3].includes(3, -1); // true


ES6 函数
在ES6之前,不能直接为函数的参数指定默认值,只能采用变通的方法。


function log(x, y) {
  y = y || 'World';
  console.log(x, y);
}
log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello World
上面代码检查函数log的参数y有没有赋值,如果没有,则指定默认值为World。这种写法的缺点在于,
如果参数y赋值了,但是对应的布尔值为false,则该赋值不起作用。
就像上面代码的最后一行,参数y等于空字符,结果被改为默认值。
为了避免这个问题,通常需要先判断一下参数y是否被赋值,如果没有,再等于默认值。
if (typeof y === 'undefined') {
  y = 'World';
}
ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面。
function log(x, y = 'World') {
  console.log(x, y);
}


log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello


什么是尾调用?
尾调用(Tail Call)是函数式编程的一个重要概念,本身非常简单,
一句话就能说清楚,就是指某个函数的最后一步是调用另一个函数。


只要使用尾递归,就不会发生栈溢出,相对节省内存。


Object.assign方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。
var target = { a: 1 };
var source1 = { b: 2 };
var source2 = { c: 3 };
Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}


Object.keys(),Object.values(),Object.entries() 




Object.keys()
ES5 引入了Object.keys方法,返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历
(enumerable)属性的键名。
var obj = { foo: 'bar', baz: 42 };
Object.keys(obj)
// ["foo", "baz"]
ES2017 引入了跟Object.keys配套的Object.values和Object.entries,作为遍历一个对象的补充手段,
供for...of循环使用。
let {keys, values, entries} = Object;
let obj = { a: 1, b: 2, c: 3 };
for (let key of keys(obj)) {
  console.log(key); // 'a', 'b', 'c'
}
for (let value of values(obj)) {
  console.log(value); // 1, 2, 3
}
for (let [key, value] of entries(obj)) {
  console.log([key, value]); // ['a', 1], ['b', 2], ['c', 3]
}


中括号运算符总是能代替点运算符。但点运算符却不一定能全部代替中括号运算符。
中括号运算符可以用字符串变量的内容作为属性名。点运算符不能。
中括号运算符可以用纯数字为属性名。点运算符不能。
中括号运算符可以用js的关键字和保留字作为属性名。点运算符不能




然后它可以当作一个栈来使用,我们知道栈的特点是先进后出,栈的基本操作是出栈和入栈:(简单的栈的理解)
var stack = [1, 2, 3];
stack.push(4);          //入栈
var top = stack.pop();  //出栈


同时它还可以当作一个队列,队列的特点是先进先出,基本操作是出队和入队:
var queue = [1, 2, 3];
queue.push(4);             //入队
var head = queue.shift();  //出队


 
因此使用 Set 可以很容易地实现并集(Union)、交集(Intersect)和差集(Difference)。
let a = new Set([1, 2, 3]);
let b = new Set([4, 3, 2]);
// 并集
let union = new Set([...a, ...b]);
// Set {1, 2, 3, 4}
// 交集
let intersect = new Set([...a].filter(x => b.has(x)));
// set {2, 3}
// 差集
let difference = new Set([...a].filter(x => !b.has(x)));
// Set {1}


Proxy可以监听对象身上发生了什么事情,并在这些事情发生后执行一些相应的操作。
一下子让我们对一个对象有了很强的追踪能力,同时在数据绑定方面也很有用处。
//创建代理以进行侦听
//定义被侦听的目标对象
var engineer = { name: 'Joe Sixpack', salary: 50 };
//定义处理程序
var interceptor = {
  set: function (receiver, property, value) {
    console.log(property, 'is changed to', value);
    receiver[property] = value;
  }
};
engineer = new Proxy(engineer, interceptor);
//做一些改动来触发代理
engineer.salary = 60;//控制台输出:salary is changed to 60


箭头函数有几个使用注意点。
(1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
(2)不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
(3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用Rest参数代替。
(4)不可以使用yield命令,因此箭头函数不能用作Generator函数。




实际开发中,经常遇到一种情况:不知道或者不想区分,函数f是同步函数还是异步操作,
但是想用 Promise 来处理它。因为这样就可以不管f是否包含异步操作,
都用then方法指定下一步流程,用catch方法处理f抛出的错误。一般就会采用下面的写法。
需要注意的是,async () => f()会吃掉f()抛出的错误。所以,如果想捕获错误,要使用promise.catch方法。
(async () => f())()
.then(...)
.catch(...)




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


(1)创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。


(2)第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。


(3)第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。


(4)不断调用指针对象的next方法,直到它指向数据结构的结束位置。


每一次调用next方法,都会返回数据结构的当前成员的信息。具体来说,就是返回一个包含value和done两个
属性的对象。其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束。
下面是一个模拟next方法返回值的例子。
var it = makeIterator(['a', 'b']);
it.next() // { value: "a", done: false }
it.next() // { value: "b", done: false }
it.next() // { value: undefined, done: true }


function makeIterator(array) {
  var nextIndex = 0;
  return {
    next: function() {
      return nextIndex < array.length ?
        {value: array[nextIndex++], done: false} :
        {value: undefined, done: true};
    }
  };
}  




createDocumentFragment()用法示例(文档碎片)
使用appendChild逐个向DOM文档中添加1000个新节点: 
for (var i = 0; i < 1000; i++) 

    var el = document.createElement('p'); 
    el.innerHTML = i; 
    document.body.appendChild(el); 



使用createDocumentFragment()一次性向DOM文档中添加1000个新节点: 
var frag = document.createDocumentFragment(); 
for (var i = 0; i < 1000; i++) 

    var el = document.createElement('p'); 
    el.innerHTML = i; frag.appendChild(el); 

document.body.appendChild(frag); 


不要小瞧这10%-30%,效率的提高是着眼于多个细节的,如果我们能在很多地方都能让程序运行速度提高10%-30%,
那将是一个质的飞跃,您也将步入骨灰级玩家的行列




                                           ember (http://www.cnblogs.com/qixuejia/p/5601877.html)
 extend,reopen。
同点: 都可以用于扩展某个类。
异点:
1. extend需要重新定义一个类并且要继承被扩展的类; 
2. reopen是在被扩展的类本身上新增属性、方法,可以扩展计算属性(相比create()方法);
到底用那个方法有实际情况而定,reopen方法会改变了原类的行为(可以想象为修改了对象的原型对象的
方法和属性),就如演示实例一样在reopen方法之后调用的Child2类的common方法的行为已经改改变了,
在编码过程忘记之前已经调用过reopen方法就有可能出现自己都不知道怎么回事的问题! 
如果是extend方法会导致类越来越多,继承树也会越来越深,对性能、调试也是一大挑战,
但是extend不会改变被继承类的行为。
计算属性最大的特点就是能自动检测变化,及时更新数据。(Ember.computed)


观察者
Ember框架本身已经大量使用观察者,但是对于大多数的开发者面对开发问题时使用计算属性是更适合的解决方案。
可以用Ember.observer创建一个对象为观察者。
Ember还为开发者提供了另一种使用观察者的方式。这种方式使你可以在类定义之外为某个计算属性增加一个观察者。
person.addObserver('fullName', function() {  
    // deal with the change…
});
然而由于同步的原因如果你的的观察者同时观察多个属性,就会导致观察者执行多次。
但是如果开发中需要设置只能执行一次观察出呢?Ember提供了一个once()方法,
这个方法会在下一次循环所有绑定属性都同步的时候执行。
  partOfNameChanged: Ember.observer('firstName', 'lastName', function() {
    //  同时观察了firstName和lastName两个属性
    //  方法partOfNameChanged本身还是会执行多次,但是方法processFullName只会执行一次
    console.log('========partOfNameChanged======');  //  
    Ember.run.once(this, 'processFullName');
  }),
观察者一直到对象初始化完成之后才会执行。 如果你想观察者在对象初始化的时候就执行你必须要手动调用Ember.on()方法。
这个方法会在对象初始化之后就执行。
var arr = ['chen', 'ubuntuvm', '1527254027@qq.com', 'i2cao.xyz', 'ubuntuvim.xyz']; 
 
arr.forEach(function(item, index) {  
  console.log(index+1 + ", " +item);
/*1, chen
2, ubuntuvm
3, 1527254027@qq.com
4, i2cao.xyz
5, ubuntuvim.xyz
*/
});
获取首元素
console.log('The firstItem is ' + arr.get('firstObject'));// chen 
获取尾元素
console.log('The lastItem is ' + arr.get('lastObject')); // ubuntuvim.xyz


//  map方法,转换数组,并且可以在回调函数里添加自己的逻辑
//  map方法会新建一个数组,并且返回被转换数组的元素
var arrMap = arr.map(function(item) {  
  return 'map: ' + item;  //  增加自己的所需要的逻辑处理
});
arrMap.forEach(function(item, index) {  
  console.log(item);
});
console.log('-----------------------------------------------'); 


var people = [  
  Person.create({ name: 'chen', isHappy: true }),
  Person.create({ name: 'ubuntuvim', isHappy: false }),
  Person.create({ name: 'i2cao.xyz', isHappy: true }),
  Person.create({ name: '123', isHappy: false }),
  Person.create({ name: 'ibeginner.sinaapp.com', isHappy: false })
];
sEvery、isAny方法
//  与every、some类似的方法还有isEvery、isAny 
console.log('isEvery = ' + people.isEvery('isHappy', true));  //  全部都为true,返回结果才是true  
console.log('isAny = ' + people.isAny('isHappy', true));  //只要有一个为true,返回结果就是true


arr.reduce(上一次数据和下一次数据作比较ES5出的)
//这道题是计算字符串里面字数最多的单次的长度。(force 5)
var str ="May the force be with you";
function findLongestWord(str) {
    var stringArr = str.split(" ");
    return stringArr.reduce(function (prev, next) {
        // 返回值为参数与当前字符串中较大的数
        // 返回值会作为下次计算的 prev 传入
        console.log(prev);
        console.log(next);
        console.log(Math.max(prev, next.length));
        return Math.max(prev, next.length);
    },0);//(0是初始值)
}
findLongestWord(str);




unless表达式类似于非操作,当model.isReading值为false的时候会输出表达式里面的内容。
<!-- 非判断 -->
{{#unless model.isReading}}
unless.....
{{/unless}}
三目运算 如果enable 为 true 则属性里面加入enable 否则加入disable
<span class={{if enable 'enable' 'disable'}}>enable or disable</span>


 this.toggleProperty('isShowingBody'); toggleProperty方法直接把isShowingBody设置为相反值。flase便true false变true






在Ember中路由和模板的执行都是有一定顺序的,它们的顺序为:主路由到子路由1到子路由2到子路由3……。
模板渲染的顺序与路由执行顺序刚好相反,从最后一个模板开始解析渲染。






       注意:模板的渲染是在所有路由执行完之后,从最后一个模板开始。
按enter键触发
{{input value=getValueKey enter/*触发方式*/="getInputValue"/*事件名*/ name=getByName placeholder="请输入测试的内容"}}


{{log model.kernelVersion}}  可以在模板中打印 model.kernelVersion 这个值
        {{debugger}} 模板中打debugger




helper就是作用就是进行数据转换 比如说Mon Sep 21 2015 23:46:03 GMT+0800 (CST)  自己在helper中写个方法转换成类似yyyy-DD-mm”
的一种方法  {{helper-name/*helper名字*/ data/*要转换的数据*/}} 详细(https://my.oschina.net/ubuntuvim/blog/509426)






ember route 


//  可以传递一个object过去  跳转到articles路由,顺便查询条件是category: 'java' 在articles界面里面只显示category: 'java'
的相关数据
this.transitionTo('articles',{ queryParams: { category: 'java' }});
 
// 这种方式不会改变路由,只是起到设置参数的作用,如果你在
//路由articles中使用这个方法,你的路由仍然是articles,只是查询参数变了。
this.transitionTo({ queryParams: { direction: 'asc' }});
 
//  直接指定跳转的URL和查询参数
this.transitionTo('/posts/1?sort=date&showDetails=true');


重定向(https://my.oschina.net/ubuntuvim/blog/511484)
     route里面的重定向
1,切换路由前获取model
       如果你想在路由切换的时候不加载model你可以调用beforeModel回调,在这个回调中实现路由的切换。


beforeModel() {
       this.transitionTo('posts');
}
2,切换路由后获取model


       有些情况下你需要先根据model回调获取到的数据然后判断跳转到某个路由上。此时你可以使用afterModel回调方法。
afterModel: function(model, transition) {
       if (model.get(‘length’) === 1) {
              this.transitionTo('post', model.get('firstObject'));
       }
}
以上就是全部路由的重定向方式,主要有4个回调:beforeModel、model、afterModel、redirect。
前面三种使用场景差别不大,redirect主要用于重定向到子路由


    controller里面的重定向
 this.transitionToRoute("systemmanage.system-services-manage"/*url*/);(界面跳转)
transition.abort()方法立即终止路由的切换.你还可以在需要的地方取出来并调用transition.retry()方法激活路由切换这个动作.


1,通过调用willTransition方法阻止路由切换.


 *********self.controllerFor("login").get('model')获取其他controller里面的model数据;


*********Ember.computed计算属性






定义model的关联关系(https://my.oschina.net/ubuntuvim/blog/518608)


       Ember的model也是有类似于数据库的关联关系的。只是相对于复制的数据库Ember的model就显得简单很多,
其中包括一对一,一对多,多对多关联关系。这种关系是与后台的数据库是相统一的。


1,一对一


       声明一对一关联使用DS.belongsTo设置。比如下面的两个model。


//  app/models/user.js
import DS from 'ember-data';
 
export default DS.Model.extend({
  profile: DS.belongsTo(‘profile’);
});
//  app/models/profile.js
import DS from ‘ember-data’;
export default DS.Model.extend({
  user: DS.belongsTo(‘user’);
});
belongsTo  一
hasMany  多




Object.defineProperty(person,'name',{
    configurable:false,//能否使用delete、能否需改属性特性、或能否修改访问器属性、,false为不可重新定义,默认值为true
    enumerable:false,//对象属性是否可通过for-in循环,flase为不可循环,默认值为true
    writable:false,//对象属性是否可修改,flase为不可修改,默认值为true
    value:'xiaoming' //对象属性的默认值,默认值为undefined
});
//value
console.log(person);//xiaoming,默认value
//writable
person.name="qiang";
console.log(person);//xiaoming,不可修改value
//enumerable
for(var i in person){
    console.log(person[i]) //无结果,不可循环
}
//configurable
delete person.name
console.log(person.name)//xiaoming,不可删除
Object.defineProperty(person,'name',{
    configurable:true //不可修改,将抛出错误
});
原创粉丝点击