第一次接触let关键字,有一个要非常非常要注意的概念就是”JavaScript 严格模式”,比如下述的代码运行就会报错:
let hello = 'hello world.'; console.log(hello);错误信息如下: let hello = 'hello world.'; ^^^ SyntaxError: Block-scoped declarations (let, const, function, class) not yet supported outside strict mode ... 解决方法就是,在文件头添加”javascript 严格模式”声明:
'use strict'; let hello = 'hello world.'; console.log(hello);声明后未赋值,表现相同
'use strict'; (function() { var varTest; let letTest; console.log(varTest); //输出undefined console.log(letTest); //输出undefined }());在箭头函数出现之前,每个新定义的函数都有它自己的this值。箭头函数不会创建自己的this,它只会从自己的作用域链的上一层继承this,总是指向函数定义生效时所在的对象。
// 1. `Person()`构造函数定义 this 作为它自己实例,而`add()`函数定义 this 作为全局对象。 function Person() { this.age = 10; foo: setInterval(function add() { this.age ++; console.log(this.age); }, 100); } new Person().foo; // 2. 通过将this值分配给封闭的变量,可以解决this问题。 (通过bind绑定作用域亦可) function Person() { let _this = this; _this.age = 10; foo: setInterval(function add() { _this.age ++; console.log(_this.age); }, 100); } new Person().foo; // 3. this正确指向person对象 function Person() { this.age = 10; foo: setInterval(() => { this.age ++; console.log(this.age); }, 1000); } new Person().foo;Babel是一个广泛使用的ES6转码器,可以将ES6代码转为ES5代码,从而在现有环境执行。可在Babel官网 (http://babeljs.io/) 查看。
使用Gulp 和 Babel 将 ES6 代码转换成 ES5 代码具体过程如下:
安装依赖
安装全局 Gulp npm install -g gulp 安装项目中使用的 Gulp npm install --save-dev gulp 安装 Gulp 上 Babel 的插件 npm install --save-dev gulp-babel 安装 Babel 上将 ES6 转换成 ES5 的插件 npm install --save-dev babel-preset-es2015Gulp 配置
gulpfile.js 的内容 var gulp = require("gulp"); var babel = require("gulp-babel"); gulp.task("default", function () { return gulp.src("src/**/*.js") // ES6 源码存放的路径 .pipe(babel()) .pipe(gulp.dest("dist")); //转换成 ES5 存放的路径 }); 如果要生成 Soucemap, 则用 gulp-sourcemaps var gulp = require("gulp"); var sourcemaps = require("gulp-sourcemaps"); var babel = require("gulp-babel"); var concat = require("gulp-concat"); gulp.task("default", function () { return gulp.src("src/**/*.js") .pipe(sourcemaps.init()) .pipe(babel()) .pipe(concat("all.js")) .pipe(sourcemaps.write(".")) .pipe(gulp.dest("dist")); });Babel 配置 在项目根路径创建文件 .babelrc。内容为:
{ "presets": ["es2015"] }执行转换 命令行中执行
gulp与 Array 增、删、改、查对比
let map = new Map(); let set = new Set(); let array = []; // 增 map.set('t', 1); set.add( { t : 1 } ); array.push( { t:1 } ); console.info( map, set, array ); // Map { 't' => 1 } Set { { t: 1 } } [ { t: 1 } ] // 查 let map_exist = map.has( 't' ); let set_exist = set.has( {t:1} ); let array_exist = array.find(item => item.t) console.info(map_exist, set_exist, array_exist); //true false { t: 1 } // 改 map.set('t', 2); set.forEach(item => item.t ? item.t = 2:''); array.forEach(item => item.t ? item.t = 2:''); console.info(map, set, array); // Map { 't' => 2 } Set { { t: 2 } } [ { t: 2 } ] // 删 map.delete('t'); set.forEach(item => item.t ? set.delete(item):''); let index = array.findIndex(item => item.t); array.splice(index,1); console.info(map, set, array); // Map {} Set {} []ES5中原型、构造函数,继承问题一直困扰我们。ES6引入了Class(类)的概念。新的class写法让对象原型的写法更加清晰、更像面向对象编程的语法,也更加通俗易懂。
class Human { constructor(name) { this.name = name; } sleep() { console.log(this.name + " is sleeping"); } } let man = new Human("Davis"); man.sleep(); //Davis is sleeping class Boy extends Human { constructor(name, age) { super() this.name = name; this.age = age; } info(){ console.log(this.name + 'is ' + this.age + 'years old'); } } let son = new Boy('Faker','8'); son.sleep(); // Faker is sleeping son.info(); // Faker is 8 years old上面代码首先用class定义了一个“类”,可以看到里面有一个constructor方法,这就是构造方法,而this关键字则代表实例对象。简单地说,constructor内定义的方法和属性是实例对象自己的,而constructor外定义的方法和属性则是所有实例对象可以共享的。
Class之间可以通过extends关键字实现继承,这比ES5的通过修改原型链实现继承,要清晰和方便很多。上面定义了一个Boy类,该类通过extends关键字,继承了Human类的所有属性和方法。
super关键字,它指代父类的实例(即父类的this对象)。子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类没有自己的this对象,而是继承父类的this对象,然后对其进行加工。如果不调用super方法,子类就得不到this对象。
ES6的继承机制,实质是先创造父类的实例对象this(所以必须先调用super方法),然后再用子类的构造函数修改this。
ES6中允许使用反引号 来创建字符串,此种方法创建的字符串里面可以包含由美元符号加花括号包裹的变量${vraible}。 ```javascript let num = Math.random(); console.log( num is ${num}); // num is xx ``` 不再通过 \ 来做多行字符串拼接,模板字符串可以多行书写: ```html $("#main").html( 今天天气很好!
产生一个随机数${num}
`); ``` 模板字符串中所有的空格、新行、缩进,都会原样输出在生成的字符串中。ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构。
用途一,交换变量的值,不再需要中间变量
let a = 1; let b = 2; [a, b] = [b, a]; console.log(a, b); // 2 1用途二,提取JSON数据
let jsonData = { id: 1, title: "OK", data: [5, 6] }; let {id, title, data:number} = jsonData; console.log(id, title, number); // 1, "OK", [5, 6]用途三,函数参数的定义
// 参数是一组有次序的值 function f([x, y, z]) { ... } f([1, 2, 3]); // 参数是一组无次序的值 function f({x, y, z}) { ... } f({z: 3, y: 2, x: 1});default很简单,就是默认值。现在可以在定义函数的时候指定参数的默认值,而不用像以前那样通过逻辑或操作符来达到目的了。
// 传统指定默认参数 function say1(name) { var name = name || 'Faker'; console.log( 'Hello ' + name ); } // ES6默认参数 function say2(name='Davis') { console.log(`Hello ${name}`); } say1(); // Hello Faker say1('Tom'); // Hello tom say2(); //Hello Davis say2('Bob'); // Hello Bob注意: say2(name='tom')这里的等号,指的是没有传这个参数,则设置默认值Davis,而不是给参数赋值。
rest参数只包括那些没有给出名称的参数;
rest参数是Array的实例,可以直接应用sort, map, forEach, pop等方法;
rest参数之后不能再有其它参数(即,只能是最后一个参数);
函数的length属性,不包括rest参数;
function fn(x, y, ...rest){ console.log(rest) } fn(1, "cat", "dog", 2); //["dog", 2] console.log(fn.length); //2Proxy可以监听对象身上发生了什么事情,并在这些事情发生后执行一些相应的操作。让我们对一个对象有了很强的追踪能力,同时在数据绑定方面也很有用处。
//定义被监听的目标对象 let man = { name: 'Davis', age: 21 }; //定义处理程序 let handle = { set(receiver, property, value) { console.log(property, 'is changed to', value); receiver[property] = value; } }; //创建代理以进行侦听 man = new Proxy(man, handle); //做一些改动来触发代理 man.age = 22; //age is change to 22 man.name = "Faker"; // name is change to FakerPromise对象状态
Promise/A+规范, 规定Promise对象是一个有限状态机。它三个状态:
pending(执行中)
Resolved(已完成)
Rejected(已失败)
其中pending为初始状态,Resolved和rejected为结束状态(表示promise的生命周期已结束)。
let val = 1; // 我们假设step1, step2, step3都是ajax调用后端或者是在Node.js上查询数据库的异步操作 // 每个步骤都有对应的失败和成功处理回调 // step1、step2、step3必须按顺序执行 function step1(resolve, reject) { console.log('步骤一:执行'); if (val >= 1) { resolve('Hello I am No.1'); } else if (val === 0) { reject(val); } } function step2(resolve, reject) { console.log('步骤二:执行'); if (val === 1) { resolve('Hello I am No.2'); } else if (val === 0) { reject(val); } } function step3(resolve, reject) { console.log('步骤三:执行'); if (val === 1) { resolve('Hello I am No.3'); } else if (val === 0) { reject(val); } } new Promise(step1).then(function(val){ console.info(val); return new Promise(step2); }).then(function(val){ console.info(val); return new Promise(step3); }).then(function(val){ console.info(val); return val; }).then(function(val){ console.info(val); return val; }); // 执行之后将会打印 步骤一:执行 Hello I am No.1 步骤二:执行 Hello I am No.2 步骤三:执行 Hello I am No.3 Hello I am No.3常用关键点:
在Promise定义时,函数已经执行了;Promise构造函数只接受一个参数,即带有异步逻辑的函数。这个函数在 new Promise 时已经执行了。只不过在没有调用 then 之前不会 resolve 或 reject。
在then方法中通常传递两个参数,一个 resolve 函数,一个 reject 函数。reject就是出错的时候运行的函数。resolve 函数必须返回一个值才能把链式调用进行下去。
resolve 返回一个新 Promise 返回一个新Promise之后再调用的then就是新Promise中的逻辑了。
resolve 返回一个值 返回一个值会传递到下一个then的resolve方法参数中。
Generator函数跟普通函数的写法有非常大的区别:
function关键字与函数名之间有一个 *;
函数体内部使用yield语句,定义不同的内部状态;
function* f() { yield 'a'; yield 'b'; yield 'c'; return 'ending'; } let fn = f(); console.log(fn.next()); // { value: 'a', done: false } console.log(fn.next()); // { value: 'b', done: false } console.log(fn.next()); // { value: 'c', done: false } console.log(fn.next()); // { value: 'ending', done: true }第一次输出fn.next()返回一个简单的对象{value: "a", done: false},'a'就是f函数执行到第一个yield语句之后得到的值,false表示f函数还没有执行完,只是在这暂停。
第二次,返回的就是{value: "b", done: false},说明f函数运行到了第二个yield语句,返回的是该yield语句的返回值'b'。返回之后依然是暂停。
第三次,第四次同理,这样整个f函数就运行完毕了。
异步操作的同步化写法 Generator函数的暂停执行的效果,意味着可以把异步操作写在yield语句里面,等到调用next方法时再往后执行。这实际上等同于不需要写回调函数了,因为异步操作的后续操作可以放在yield语句下面,反正要等到调用next方法时再执行。所以,Generator函数的一个重要实际意义就是用来处理异步操作,改写回调函数。
例子:假设我在测试服务器的某目录建了4个文件,分别是'test.html'、'a.html'、'b.html'、'c.html',后三个文件的文件内容跟文件名相同,现在我编辑'test.html'的代码,想要先ajax-get相对网址'a.html',然后再回调里ajax-get相对网址'b.html',然后在回调里ajax-get相对网址'c.html'。
function req(url) { $.get(url, function(res){ it.next(res); }); } // 生成器函数 function* ajaxs() { console.log(yield req('a.html')); console.log(yield req('b.html')); console.log(yield req('c.html')); } var it = ajaxs(); // 遍历器对象 it.next(); // a.html // b.html // c.html强调:只有当yield后面跟的函数先执行完,无论执行体里面有多少异步回调,都要等所有回调先执行完,才会执行等号赋值,以及再后面的操作。这也是yield最大的特性。
export用于对外输出本模块(一个文件可以理解为一个模块)变量的接口;
import用于在一个模块中加载另一个含有export接口的模块。
导出一组对象
导出模块文件app.js:
class Human{ constructor(name) { this.name = name; } sleep() { console.log(this.name + " is sleeping"); } } function walk() { console.log('i am walking'); } function play() { console.log('i am playing'); } export { Human, walk }模块导出了两个对象:Human类和walk函数,能被其他文件使用。而play函数没有导出,为此模块私有,不能被其他文件使用。
main.js导入app.js模块
import { Human, walk } from 'app.js';Default导出 使用关键字default,可将对象标注为default对象导出。default关键字在每一个模块中只能使用一次。
... //类,函数等 export default App;main.js导入app.js模块
import App from 'app.js';解释: 箭头函数会强制绑定当前环境下的 this。
示例: // good let foo = { bar(x, y) { return x + y; } }; // bad let foo = { bar: function (x, y) { return x + y; } };示例:
// good let foo = [...foo, newValue]; let bar = [...bar, ...newValues]; // bad let foo = foo.concat(newValue); let bar = bar.concat(newValues);转载于:https://www.cnblogs.com/mapleChain/p/11527711.html
相关资源:ES6总结及面试题集合