【愣锤笔记】温故而知新--ES6拾遗
栏目: JavaScript · 发布时间: 5年前
内容简介:ES是一把利器,也是一匹野马。扎实好ES基础,会让人如虎添翼,你还在犹豫什么。数组解构
ES是一把利器,也是一匹野马。
扎实好ES基础,会让人如虎添翼,你还在犹豫什么。
const和let
- 暂时性死区:块级作用域内,在const和let声明变量之前该变量都是不可使用的。不管外部是否用同名变量已经声明。
- 不存在变量提升
- 不允许重复声明
- es5没有块级作用域,es6中使用const或let则自动会形成块级作用域
- 块级作用域可以达到和IIFE相同的效果
// IIFE 写法 (function () { var num = ...; ... }()); // 块级作用域写法 { let num = 123; } 复制代码
let a = function(){}
if (true) let a = 1; // 不存在块级作用域 复制代码
- const常量,指的是常量对应的内存地址不得改变,而不是对应的值不得改变。所有把引用类型的数据设置为常量,其内部的值是可以改变的,因此需要小心:
const a = {}; a.b = 13; // 没毛病,不会报错 cont arr = []; arr.push(123); // 也没毛病 // 如果需要,可以将对象冻结 const a = Object.freeze({}) // 除了对象本身,其属性也应该冻结 var constantize = (obj) => { Object.freeze(obj); Object.keys(obj).forEach( (key, i) => { if ( typeof obj[key] === 'object' ) { constantize( obj[key] ); } }); }; 复制代码
- es5的var/function声明的全局变量会自动挂载到全局对象中,但是es6的const/let/class声明的全局则不会。
解构
数组解构
- 解构不到时变量的值为undefined
var [a, c] = [2]; a // 2 c // undefined 复制代码
- 等号右侧只要具有iterator解构的数据都可以进行数组解构
- 解构可以设置默认值,只有严格等于undefined时才会使用默认值:
let [a, b = 1] = [2, null] // 1, null let [a, b = 1] = [2] // 2 1 let [a, b = 1] = [2, undefined] // 2 1 复制代码
- 解构可以使用其他变量,但是需要先声明
let [x = y, y = 1] = []; // 报错,使用y时,y还未声明 let [x = 1, y = x] = []; // x=1; y=1 复制代码
对象解构
- 数组按照位置解构,对象按照同名属性解构
- 如果等号右侧不是对象或数组,解构时会先将其转换成对象。由于undefined/null无法转换成对象,对其解构时会报错:
const {a, b} = undefined || []; // 个人推荐这种这种写法避免报错,a undefined, b undefined 复制代码
解构其他用法
- 解构函数返回的多个值
// 函数返回多个值只能通过数组或者对象的形式返回 const func = () => [1, {a: 1}, [1,2,3,4]]; const [first, second, third] = func(); // first 1,second {a: 1}, third [1,2,3,4] const func = () => { return { first: 1, second: { a: 1}, third: [1, 2, 3, 4, 5] } }; const {first, second, third} = func(); // first 1, second {a: 1}, third [1, 2, 3, 4, 5] 复制代码
- 从导入的模块进行解构
import { moduleA, moduleB } from 'someModule'; 复制代码
字符串
- 字符串模板中,可以使用变量/函数等
var func = function () {return '123'} var val = '456' console.log(`this is string ${func()} + ${val}`) // this is string 123 + 456 复制代码
toString
// 字符串是否包含某个字符串 // 可接受第二个参数表示从下标n开始往后查找 var str = 'abcdefg'; str.includes('c', 1); // true // 字符开始是否包含某个字符串 // 第二个参数表示从下标n开始是否包含某个字符串 str.startsWith('b') // false str.startsWith('b', 1) // true // 结尾是否包含 str.endsWith('b') // false str.endsWith('b', 2) // true,表示前n个字符的结尾是否包含查询的字符串 str.endsWith('b', 1) // false // 返回一个重复n次的新字符串 str.repeat(2) // 'abcdefgabcdefg' // 补全字符串(返回补全后的新字符串,不改变原字符串) var str = 'a'; str.padStart(4, 'b') // bbba,在前面补 str.padEnd(4, 'b') // abbb。 后补 str.padStart(4) // ' a', 默认补空格 // 消除前/尾空格 (返回新字符,不改变原字符串) var str = ' a '; str.trimStart() // "a " str.trimEnd() // " a" 复制代码
数值
- 新增方法,建议放在Number对象上使用(标准也在逐步减少全局方法,使语言逐渐模块化)
// 判断有限数 Number.isFinite(123) // true Number.isFinite(Infinity) // false Number.isFinite('123') // false,非数字类型一律返回false // parseInt和parseFloat移植到了Number对象上 Number.parseInt() Number.parseFloat() // 判断整数 Number.isInteger(2) // true Number.isInteger(2.0) // true js中整数和浮点数采用的是同样的储存方法,所以 2 和 2.0 被视为同一个值 复制代码
JavaScript 采用 IEEE 754 标准, 数值存储为64位双精度格式,
数值精度最多可以达到 53 个二进制位(1 个隐藏位与 52 个有效位)。
如果数值的精度超过这个限度,第54位及后面的位就会被丢弃,这种情况下,Number.isInteger可能会误判。
-
js中的最小常量, 表示1与大于1的最小浮点数的差值
Number.EPSILON
// 2.220446049250313e-16引入该常量是为浮点数计算,设置一个误差范围:
0.1 + 0.2 === 0.3 // false // 可以设计如下函数,误差小于某个差值时自动忽略 const isEqualInAcceptErrorRange = (a, b) => Math.abs(a - b) < Number.EPSILON * Math.pow(2, 2); isEqualInAcceptErrorRange(0.1 + 0.2, 0.3) // true 复制代码
- Math扩展
Math.trunc(1.111) //1, 返回去除小数后的值,等于1.111 | 0 Matn.sign(1.111) // +1,判断一个数是正数还是负数,-1负数,0,-0,其他值反水NaN 复制代码
函数
- 函数可以写默认参数
// 不能有同名函数参数 // 已声明的参数变量在函数体内不可以用let/const重复声明 // 参数表达式是惰性求值的,每次调用函数时都会重新计算参数的值(也只会在每次调用时才计算参数的值) function func(a = 1, b = 2, c) { } 复制代码
- 与对象解构一起使用
// 与对象解构一起使用,但是如果函数调用时没传参数, // 那么从undefined进行解构则会报错 const func = ({ x, y = 1 }) => {}; func({}) // 正常 func() // 报错 // 与对象解构+函数参数默认值一起使用 const func2 = ({ x, y = 1 } = {}) => {}; func2() // 正常 复制代码
- 一旦使用了函数默认参数,函数的length属性将失真,length本质是函数预期传入的参数
- rest参数
// 只能使用在最后,后面不能再有参数 // rest参数是一个真正的数组,arguments是类数组 const func = function (...args) { return args // 等同于 // return Array.prototype.slice.call(arguments) } 复制代码
- 箭头函数返回对象时必须加小括号
const func5 = () => ({a: 1, b: 2}) 复制代码
- 箭头函数的this指定义时的上下文中的this(因为箭头函数没有this,所有this自然是其外部的this),而普通函数中this则指向运行时的上下文
- 箭头函数不可以作为构造函数使用
- 箭头函数无arguments对象,可以哟过rest参数替代
- 不应该使用箭头函数的场景:
var cat = { count: 0, add: () => { // 由于对象构不成单独的作用域,所以如果写成箭头函数, // 则this在此时指向全局了,而不是该对象 this.count ++ } } cat.add() // cat.count并没有变化 // 事件中需要this指向当前元素时,也不可以使用箭头 button.addEventListener('click', () => { // 得不到期望的结果 this.classList.toggle('on'); }); 复制代码
- 箭头函数可以嵌套
// 例如定义一个管道函数,即前一个函数返回的值作为下一个函数的参数 const pipeline = (...funcs) => v => funcs.reduce((a, b) => b(a), v); 复制代码
数组
- 数组扩展运算符只能用在函数中,用于将数组转换成逗号分割的参数序列
- 扩展运算符可以替代apply来进行传递参数
var arg = [1, 2, 3, 4] func.apply(null, arg) func(...arg) Max.max.apply(null, arg) Math.max(...arg) 复制代码
- 合并数组
// 注意是浅拷贝 var arr = [...arr1, ..arr2, ..arr3] 复制代码
- 所有定义了Iterator接口的数据都可以使用扩展运算符转换成真正的数组
[...Iterator] 复制代码
- Array.from()将累数组对象和Iterator接口的数据解构转化成真正的数组
var divs = document.getElementsByTagName('div') Array.from(divs) // 接受第二个参数,用于处理每一项数据,类似于map Array.from(arrayLike, x => x * x); 复制代码
- Array.of()返回参数组成的新数组
- 扩展方法
// 拷贝数组部分内容覆盖到数组其他位置 // 参数,覆盖开始的位置(含)/拷贝开始的位置(含)/拷贝结束的位置(不含) // 负数表示倒数 [1,2,3,4,5].copyWithin(1, 2, 3) // [1, 3, 3, 4, 5] // 找到某个值,否则返回undefined // 接受第二个参数绑定回调函数的上下文 [1,2,3,4].find((e) => e === 3) // 3 // 找到符合条件的第一个值的下标,否则返回-1 var arr = [1,2,3,4,5] arr.findIndex(e => e > 4) // 4 // 填充数组 // 参数:填充值,填充开始位置,填充结束位置(不含) // 只会填充已有的值 [1,3,4,5,6,7].fill('a', 3, 11111) // [1, 3, 4, "a", "a", "a"] // 遍历键名,arr.keys() var arr = ['a', 'b' ,'c' ,'d'] for(let index of arr.keys()) { console.log(index) } // 遍历值,arr.values() var arr = ['a', 'b' ,'c' ,'d'] for(let index of arr.values()) { console.log(index) } // 遍历键值对,arr.entries() var arr = ['a', 'b' ,'c' ,'d'] for(let index of arr.entries()) { console.log(index) } // 是否包含某个值 [1, 2, 3].includes(2) // true // falt数组,默认flat一层 [1, [2,3], [[4, [5, [6]]]]].flat() // 只有第一层 嵌套的被flat // 等价于 [1, [2,3], [[4, [5, [6]]]]].flat(1) // 接收一个参数表示要flat的层数,如果想flat全部层级 [1, [2,3], [[4, [5, [6]]]]].flat(Infinity) // [1, 2, 3, 4, 5, 6] Infinity表示flat全部层级 // flatMap先对数组每项的值进行回调函数的处理后再flat // 只能拉伸一层 [2, 3, 4].flatMap((x) => [x, x * 2]) // 空位 // 空位的值不是undefined,[,,,,]或者new Array(5)都会产生空位 // es6明确规定空位会转换成undefined 各种方法对空位的处理不一致,所以要绝对避免开发中出现空位 复制代码
对象
- 表达式作为对象的属性名
var name = 'xiaoming'; var o = { ['liu'+ name]: 1 } o.liuxiaoming // 1 复制代码
- 获取属性的描述信息
Object.getOwnPropertyDescriptor({a: 1}, 'a') 复制代码
- 对象属性的遍历
var o = {a: 1} for/in // 自身和继承的除symbols外的所有属性 Object.keys(o) // 自身的除Symbols和不可枚举外的所有属性 Object.getOwnPropertyNames(o) // 自身的除Symbols外所有属性 Object.getOwnPropertySymbols(o) // 所有Symbols属性 Reflect.ownKeys(o) // 自身所有属性 复制代码
- super关键字
// es6新增super指向对象的原型对象 // 只能用在对象的方法中,否则报错 var o = {a: 1} var o2 = { a: 2, b () { return super.a } } // 将o2的原型设置为o Object.setPrototypeOf(o2, o) o2.b() // 1 复制代码
- 对象的解构,用法和数组一样,也是浅拷贝,不能解构undefined/null
- 解构可以解构到原型上继承到值,但是扩展运算符到解构无法解构到原型上继承到值
var o = Object.create({a: 1}) var {a} = o // a 1 var {...rest} = {} // rest {} 复制代码
- 对象到方法
// 是否相等 Object.is(a, b) // 判断两个值是否完全相等,比===强在+0-0,NaN的判断 // 对象浅拷贝 Object.assign(target, o2, o3, o4) // 将o234合并到target对象上,同名属性替换 // 获取对象单个属性的描述对象 Object.getOwnPropertyDescriptor(o2, 'a') // 获取对象所有属性的描述对象,注意两者的区别,一个加s,一个没有s Object.getOwnPropertyDescriptors(o2) // 设置对象的原型 var o1 = {} var o2 = {a: 1} Object.setPrototypeOf(o1, o2) // 把o1的原型设置为o2 // 读取对象的原型对象 Object.getPrototypeOf(o1) 复制代码
Symbol
- Symbol是新增的第七种原始数据类型,凡是属性名为Symbol类型的,可以保证不与其他属性名冲突
var s1 = Symbol() var s2 = Symbol('s2') // 为s2增加一个描述 s2.description // 获取s2的描述 // 作为属性名使用 var o = { [s2]: 456 } o[s1] = 123 // 不能使用点运算符,否则会认为是字符串 复制代码
-
Symbol不会被
for...in
、for...of
循环,也不会被Object.keys()
、Object.getOwnPropertyNames()
、JSON.stringify()
返回 -
Symbol.for()/Symbol.keyfor()
// Symbol.for接收一个参数,每次先搜索有没有存在这个Symbol,有则返回,无则创建再返回 var s1 = Symbol.for('s1') var s2 = Symbol.for('s1') s1 === s2 // Symbol.keyFor(s1)返回一个Symbol.for()登记过的值的key // 参数是一个Symbol.for()注册的数据类型 Symbol.keyFor(s1) // "s1" 复制代码
- 内置的Symbol值
// 对象的Symbol.hasInstance指向对象内部一个方法,使用instanceOf判断一个变量是否是该对象的实例时,会自动调用该对象的这个方法去验证 // 比如,foo instanceof Foo在语言内部,实际调用的是Foo[Symbol.hasInstance](foo) class MyClass { [Symbol.hasInstance] (arr) { return arr instanceof Array } } [] instanceof new MyClass() // true 'aa' instanceof new MyClass() // false var o = {} o instanceof new MyClass() // false 复制代码
Set
// 类似与不能重复的数组 var s = new Set() var s2 = new Set([1, 2, 3]) s2.add(4) // 利用Set去重 [...new Set([1,2,3,4,5,5,5,5])] // 获取长度 s.size // 删除 s.delete(1) // 删除成功返回true,否则返回false // 清除所有 s.clear() // 检测是否含有 s.has(2) // 返回true/false // 转换成数组 Array.from(new Set([1,2,3,4])) // Set可以直接使用forEach方法 // filter/map等只能间接使用 s.forEach((value, key) => {}) s = new Set([...s].filter(e => {})) // 利用Set实现并集 var s1 = new Set([1,2,3]) var s2 = new Set([2,3,4]) new Set([...s1, ...s2]) // 交集 new Set([...s1].filter(e => s2.has(e))) 复制代码
Map
// 键值对的集合,类似于对象,但是键不再只是字符串 var m = new Map() var m2 = new Map([[1, 2], ['key', 345]]) // 如果键是基本数据类型,只要严格相等则认为是同一个键 // 如果键是引用类型,只有是同一个引用才判定是同一个键 // 新增数据,set返回当前map对象,所以支持链式调用 // 有则覆盖,无则生成 m.set(2, 3) m.set(['aa'], 1234) m.set({}, 543).set('b', 123) // 读取键值,无则返回undefined m.get(2) // 获取长度 m.size // 删除 m.delete(键名) // 返回true/false // 清除所有 m.clear() // 判断是否含有某个键 m.has() // 遍历,遍历顺序就是插入顺序 m.forEach() m.keys() m.values() m.entries() 复制代码
Proxy
- Proxy代理,给原目标设置一个代理器来控制访问。代理器只对Proxy返回的实例有效,对原目标对象无效
var obj = {x: 1} var proxyObj = new Proxy(obj, { get () { return 2222; } }) console.log(proxyObj.x); // 2222 // Proxy的实例可以作为其他对象的原型对象使用 var obj2 = Object.create(proxyObj) console.log(obj2.x) // 2222 复制代码
- 代理器拦截设置(即new Proxy的第二个参数)
// get拦截对象属性的读取 // 接收三个参数:拦截目标,拦截属性,proxy实例 // 例如,利用proxy生成dom节点的函数 const dom = new Proxy({}, { get (target, key, proxySelf) { return function (attrs = {}, ...children) { let el = document.createElement(key); for (let i of Object.keys(attrs)) { el.setAttribute(i, attrs[i]) } for (let child of children) { if (typeof child === 'string') { child = document.createTextNode(child) } el.appendChild(child) }; return el; } } }); // 调用生成节点 const el = dom.div({}, 'Hello, my name is ', dom.a({href: '//example.com'}, 'Mark'), '. I like:', dom.ul({}, dom.li({}, 'The web'), dom.li({}, 'Food'), dom.li({}, '…actually that\'s it') ) ); document.body.appendChild(el); // set拦截对象属性的设置 // 接收四个参数:目标对象,设置的属性,属性值,proxy实例 var proxyHandler = { get(target, key, value, proxySelf) { if (key[0] === '_') { throw new Error(`${key}是一个内部属性`) } return target[key] }, set(target, key, value, proxySelf) { if (key[0] === '_') { throw new Error(`${key}是一个内部属性`) } target[key] = value } } var objProxy = new Proxy({}, proxyHandler) objProxy.a = 123 objProxy.a // 123 objProxy._a // 报错内部属性 // has拦截HasProperty操作,例如典型的'a' in obj,但是不对for/in生效 // 第一个参数源对象,第二个参数属性 // construct 拦截new操作 // 三个参数:原目标对象/函数参数/proxy实例本身 var Person = function() {} var PersonProxy = new Proxy(Person, { construct (target, args, proxySelf) { console.log('拦截了new操作') return new target(args) } }) new PersonProxy() // deleteProperty拦截delete操作,如果返回false则无法删除 // 参数 target,key // defineProperty拦截Object.defineProperty操作 // 返回false则该操作无效 // 参数: target, key, proxySelf // getOwnPropertyDescriptor拦截Object.getOwnPropertyDescriptor()操作 // 其他拦截方法 - getPrototypeOf - isExtensible - ownKeys - preventExtensions - setPrototypeOf // 取消proxy的代理器,用于在完成代理后,立即收回代理权 // Proxy.revocable返回一个对象,该对象的proxy属性是该代理的实例,revoke是一个可以收回proxy代理权的函数。 var Person = function() {} var {proxy, revoke} = Proxy.revocable(Person, { construct (target, args, proxySelf) { console.log('拦截了new操作') return new target(args) } }) new proxy() // 正常实例化 revoke() // 取消其proxy的代理权 new proxy() // 无法实例化 // proxy实例之后,实例中的this指代proxy实例 复制代码
class类
const getname = "GET_USER_NAME"; class Person { // 实例属性也可以写在最顶部,此时不需要加this time = new Date(); // 构造函数里面的内容 constructor (x, y) { this.x = x; this.y = y; console.log(new.target === Person) // } // 类原型上的方法 toString () { console.log(this.x + ' - ' + this.y); } // 静态方法,只有类能访问的,实例不能访问 // 因此这里的x,y都是undefined,因为x,y属性都是实例属性 static toString () { console.log('静态方法:', this.x + ' - ' + this.y) } // 可以使用表达式命名 [getName] () { } } const p1 = Person('mack', 25); p1.toString(); // mack - 25 Person.toString() // 静态方法: undefined - undefined 复制代码
- class中的this指向类的实例
- 类的属性名可以采取表达式
- 可以对属性设置get/set拦截
- 如果不定义constructor函数,则类会默认增加一个constructor函数,并返回类的实例(即this)。如果在constructor中显示返回一个对象,会导致实例结果不再是类的实例。
- es6为new引入了target属性,返回实例的构造函数,如果不是通过new调用的,则返回undefined
function Person () { // 以前的写法 if (!(this instanceof Person)) { return new Person() } // 现在可以通过new.target if (new.target !== Person) { return new Person() } } 复制代码
extends继承
// 定义父类 class Parent { constructor (name, age) { this.name = name; this.age = age; } static getName () { console.log(this.name); return this.name; } getAge () { console.log('age: ', this.age); } } // 定义子类,子类继承父类 class Child extends Parent { constructor (...arg) { // 通过super继承父类的属性和方法 // 必须通过super继承后,才能使用子类自己的this // 否则报错,得不到子类的this对象 super(...arg); // es5的实现方式Parent.apply(this, arg) // this.name = name; // this.age = age; } } var p1 = new Child('xiaoming', 24); Child.getName() // 父类的静态方法,会被子类继承 p1.getAge() // 父类的原型方法,会被子类的实例继承 复制代码
Object.getPrototypeOf(Child)
导入导出
const a = 5 const b = function(){} const c = class {} // 导出 export const age = '123' export { a, b, c } // 导出多个,建议采取该方式,放在文件底部,一眼就看清楚有哪些导出 export { a as bobyAge } // 导出时定义别名 // 导入 import { a, b, c } from './someJs' // 需要和导出的变量名对应 import { a as otherName } './someJs' // 可以起一个别名 import * as types from './someJs' // 导入所有变量,并起一个types变量名 复制代码
- 多次导入同一个文件,只会执行一次
- export导出的内容,import时需要加{}
- import在编译时执行,不是在运行时执行,因此不能使用变量
- import存在声明提升,因此最好不要和require时使用还对其有依赖关系
const a = 123; const f = function () {} const o = {} const C = Class {} // 默认导出 // 一个模块文件只能有一个默认导出 export default a; // 或 export default f // 或 export default { a, f, o, C } // 导入 import newName from './some.js' // 导入时不用和导出的变量名对应,可以随便起一个名字 import _ from 'underscore'; // 同时导入默认内容和其他内容 import o, {a, b, c} from './some.js' 复制代码
- 导入导出的复合写法
// 先导入后导出,此时并没有把a,b导入到当前模块,只相当于对外做了一个转发 export {a, b} from './some.js' // 也可以使用别名 export {a as otherName} from './some.js' 复制代码
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
编译原理
Alfred V.Aho、Jeffrey D.Ullman、Ravi Sethi / 李建中 / 机械工业出版社 / 2003-8 / 55.00元
《编译原理》作者Alfred V.Aho、Ravi Sethi和Jeffrey D.Ullman是世界著名的计算机 科学家,他们在计算机科学理论、数据库等很多领域都做出了杰出贡献。《编译原理》 是编译领域无可替代的经典著作,被广大计算机专业人士誉为“龙书”。《编译原理》一 直被世界各地的著名高等院校和科研机构(如贝尔实验室、哥伦比亚大学、普 林斯顿大学和斯坦福大学等)广泛用作本科生和研究生编译原理......一起来看看 《编译原理》 这本书的介绍吧!