ES6早先记录(较乱)

2020-01-23 11:52栏目:龙电竞官网
TAG:

图片 1

转码器:babel,允许用ES6的语法来写程序,而不用考虑现有环境是否支持。

本文转自:作者:小胡子哥《JavaScript 权威指南》这本书从第四版开始,一直到第六版,每个版本我都逐字逐句读过几遍,然而每一遍下来的感受却完全不一样。上上周的周一,再次翻开了这本犀牛书,这一次我是带着批判精神和研究精神过来的,所以看的时候也写下了一些感受和笔记,都是些容易被忽略的点,部分内容犀牛书上不一定有提到。之前都发在微博上,稍微整理了一番,放在这里,方便阅读。一些小点语句/表达式换个角度理解语句和表达式:表达式不会改变程序的运行状态,而语句会。还有一种叫做表达式语句,可以理解为表达式和语句的交集,如 ({a:1})、"use strict;"等,我觉得没必要死扣,意义不大。字符集ES3 要求 JS 必须实现 Unicode 2.1 及后续版本,而 ES5 只要求支持 Unicode 3 及后续版本。Unicode 字符 2005 年超过了十万字符,至今仍在不断增修,最新版本是 8.0。分号如果你写 JS 代码不喜欢带分号,而又搞不清什么时候必须加分号,可以这么做:在以 “(“、”[“ 、”/“、”+”、”-“ 开头的语句前面都加上一个分号,如 ;(a + b).toString()。进制ES5 严格模式中禁止使用八进制。目前各种引擎对 JS 的实现是存在差异的,部分支持八进制,部分不支持。八进制被禁止的原因:String 和 Number 之间经常被相互转换,而以 0 开头的八进制数据特别容易让人迷惑,也容易让机器迷惑,比如 09 是该被转换成 9 还是直接报错?十六进制不存在这个问题,如 0x98。更多信息参阅 这里。精度JS 采用 IEEE-754 浮点数表示法,这是一种二进制表示法,由于精度原因 JS 不能表示所有的实数。它能展示的浮点数个数是有限的,比如它不能准确地表示三分之一的数值字面量。这也导致了它在浮点数的计算上存在误差,如 0.3-0.2 != 0.2-0.1,因为在计算的过程中,存在数据的溢出,丢失了精度。null/undefined系统级、出乎意料的或者类似错误的值的空缺使用 undefined,而程序级、正常的或意料之中的值的空缺使用 null。平时编程给变量赋值时,不要使用 undefined 而应该用 null。值得注意的是 ES3 中的 undefined 是可以被重新赋值的,ES5 修复了这个 bug。通常我们使用 void 0 来还原/代替 undefined 的值。evaleval 是个不好把握的东西,它在 ES3 中更像是 Function,而在 ES5 中更像是一个运算符。实际上 ES3 中也不允许给 eval 设置别名,然而很多实现却依然允许,并将其作为全局代码来执行,浏览器尤其是 IE 对它实现相当混乱,没有什么规律可循,不过 IE 中提供了一个 execScript 函数,类似全局的 eval,这个函数每次执行都会返回 null。需要使用eval的场景并不多,尽量少用,一般需求使用new Function 就能满足。引用删除属性存在的坑:a = {n: {x: 2}}, b = a.n; delete a.n; 这段代码执行之后,b.x 依然等于 2,原因是 {x:2} 这个对象被 a 和 b 同时引用,delete 指令只删除了 a 对它的引用,b 上的引用依然存在。这种问题有可能造成内存泄漏。Object 扩展Object的freeze方法过于严格;defineGetter/lookupGetter 和对应的 Setter 是很好用的属性。toLocalString如图,你可能还不知道 JavaScript 的 toLocaleString 还可以这么玩。this语义this 上下文只存在两种语义,一种是被当作方法调用,this 指向调用它的对象;一种是作为函数调用,指向 Global 对象。它没有作用域的限制,如下图所示,a 由于是作为函数被调用,所以它指向的是 window,故而返回 false。类型JavaScript 可以被调用执行的均为 Function 类型,但是也存在可调用的 Object,如低版本 IE 中的一些宿主对象:document.getElementById、alert 等,在很多浏览器中 typeof RegExp 同样是 Object。这绝对是一个不标准的实现,在浏览器摒弃/修正这些错误类型之前应该尽量少依赖它们。IE8 getter/setterObject.defineProperty虽然是ES5的东西,早在IE8就已经支持了,但支持得并不完善,比如 writable、enumerable、configurable 这些配置项设置就无效,IE8 下主要支持 getter/setter。JSON.stringifyJSON.stringify 接受三个参数,很多人都知道第三个参数可以设置空白字符来美化输出,但是你可能不知道第二个参数的作用,它为 {Array|Function} 类型,如果为 Array 则用于过滤 key,如果为 Function 则可以对 value 做处理,如图所示。SymbolES6 中添加了一种新的数据类型,Symbol,它是一种原始数据类型,具备对象的特性,并可以指向同一个引用,能够作为对象的 key 但不可枚举,内置的 Symbol 会影响程序的执行,Symbol.iterator 是个举足轻重的符号,能够让元素具备迭代属性,花样很多。附图见:伪数组添加 Symbol.iterator 的几个办法:鸭式辨型的 iterator 函数、yield 函数和直接使用 Array 的遍历符号。附图见:Set/WeakSetSet/WeakSet 这种数据结构,不能说没用,但确实也没啥大用,前者就是个不允许出现重复成员的数组,顺便还带了点 ES6 的特性,后者虽说可以一定程度上防止内存泄漏,但是也容易出错,比如某个引用已经被垃圾回收了,再去使用它可能就返回 null。它们都是 ES6 的配套产物。而 Map/WeakMap 倒是两个非常不错的设计,常规的 Object 结构都为 String-Val 键值对,而它扩展为 AllType-Val,任意类型都可以作为它的 Key,无论是服务端编程还是客户端编程,这个属性都带来了极大的便利性。正则理解正则零宽的含义:正则中所谓的零宽断言,类似于锚点字符,它们匹配指定的位置而不会匹配内容,如 ^ 匹配开头,$ 匹配结尾,b 匹配单词边界;(?=p) 匹配「接下来的字符与 p 匹配」的位置,(?!p) 匹配「接下来的字符不与 p 匹配」的位置。b 字符匹配单词边界,实际上就是匹配 w 与 W 之间的位置。很少会有人用到 B,它匹配的是非单词边界位置,简单理解就是 w w 之间位置或者 W W 之间位置。持续学习和分享…内容都是片段化的分享,比较多,也比较杂,就没有全部列举出来,感兴趣的同学可以 follow 我的 微博,我的想法和笔记都会在上面同步。感受在这之前犀牛书已经翻阅了差不多六七遍,很多内容都已经深深地刻在了脑海里,但时间久了也会忘记些,时而巩固复习下,毕竟是前端最基础部分。带着问题去看书,收获是完全不一样的。犀牛书不难啃,难的是你对这些知识点的理解深度。原文链接:

EcmaScrip是规范,JavaScript是实现。
Es6包含ecma2015 ecma2016 ecma2017,一般是指ecma2015。

let 和 const

顶层对象的属性:
ES5 顶层对象的属性与全局变量是等价的。
ES6 开始全局变量不都是顶层对象的属性了,只有var、function定义的才算,let、const、class定义的不算顶层对象的属性。

const与let一样:

  • 块级作用域内有效
  • 声明不会提升,需先声明再使用(暂时性死区)
  • 不允许重复声明
  • const声明只读常量,声明对象时指对象地址不可变,但对象内容可改变(需立即初始化)
  • 另:对象冻结可使用Object.freeze( obj )。

ES5声明变量有2种方式:var + function
ES6声明变量有6种方式:var + function + let + const + import + class

ES5只有全局+函数作用域,无块级作用域(导致:内部变量覆盖外部变量、for计数变量外泄为外部变量等)
ES6 let只在块级作用域内有效,可以替换立即执行函数了IIFE。{let a;…..}

ES5不允许在块级作用域中声明函数,但是浏览器真正实现是:允许声明函数,而且会提升至作用域前面。

ES6明确允许在块级作用域中声明函数,而且浏览器环境和非浏览器环境表现不同:

浏览器环境:相当于var声明,会提升(提升至所在全局||函数作用域头部),赋值延后(函数声明会提升至所在块级作用域头部)。

  • 非浏览器环境:相当于let声明,不会提升,只在当前块级作用域有效。
    所以:为了表现一致,尽量不在块级作用域内声明函数,若必须声明,则使用 函数表达式方式 ,而非函数声明方式。

let为js新增了块级作用域,声明变量,只在当前代码块内有效。
立即执行函数表达式IIFE(Immediately-Invoked Function Expression):(function(){})();

变量的解构赋值

解构赋值的规则:只要等号右边不是对象,就现将其转换成对象。
解构赋值:数组、对象、字符串、数值、布尔值、函数参数等的解构赋值。

** 对象的解构赋值 **
模式不会被赋值,被赋值的是模式后面的变量:

let {loc: first, glo:last} = {loc:123, glo: 456};//loc和glo是模式,first和last才是真正赋值的变量

对于已经声明的变量进行解构赋值时,需要加上小括号,因为不加的话,js会将行首的花括号解释为代码块,引起语法错误。

let x;
{x} = {x:1}; //SyntaxError: syntax error
({x} = {x:1}); //正确

解构赋值的几个好处:

  1. 提取JSON数据的值;
  2. 函数参数定义默认值;
  3. 提取某对象或模块的方法;

字符串的扩展

对字符串进行操作的时候,要注意常规字符(两字节)和辅助字符(四字节)的区别。
unicode是世界上所有的字符集,是一种二进制的表示方法,但是并没有规定存储方式。UTF-8和UTF-16、UTF-32就是其具体的存储实现,具体区别可以阅读阮一峰老师的两篇文章,参考:

  • http://www.ruanyifeng.com/blog/2014/12/unicode.html
  • http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html

字符串的扩展重点:

  • 码点大括号表示法: 'u{20BB7}'
  • 对于传统for循环无法识别的大于0xFFFF的码点,for...of可以正确识别
  • normalize() repeat() includes() startsWith() endsWith()等函数
  • 模板字符串:`xxxxxx ${variable} yyy ${func()}` //引入变量时使用${}

正则的扩展

正则表达式新增flags属性,返回修饰符

  • ES5:/abc/ig.source 返回正文abc
  • ES6:/abc/ig.flags 返回修饰符ig
  • 新增修饰符u和y,u代表可以处理unicode大于0xFFFF的字符,y类似于g,只是y是从剩余字符中的第一个位置开始匹配,g是只要剩余字符中匹配即可。
  • /abc/ig.sticky是否设置了粘连属性y
  • 提案:RegExp.escape()函数对字符串进行转义
  • 提案:/abc/s.dotAll属性,修饰符/s:点代表一切字符

数值的扩展

  • 二进制和八进制表示法(0B和0O)
  • 新增一些方法:Number.isNaN() .isFinite() .parseInt() .parseFloat() .isSafeInteger()
  • 未解决js浮点运算的bug,引入一个极小常量Number.EPSILON
  • Math对象扩展了一些函数:.trunc() .sign()等

数组的扩展

  • 增加一些方法:Array.from() 将类数组转换成数组;Array.of()将一组值转换成数组,解决Array构造函数参数不等的歧义问题
  • [].find() .findIndex() .entries() .keys() .values() .includes()等方法
  • 尽量避免出现空位,防止表现不一致引起问题

JS中数组的迭代方法:

方法 举例
forEach:对数组中的每一项执行操作 arr.forEach(function(v, i, arr){return v+1;})
every:每一项都满足某条件则返回true if(arr.every(function(v,i,arr){return v > 2;})){}
some:只要有一项满足某条件则返回true if(arr.some(function(v,i,arr){return v > 2;})){}
map:对数组每一项进行操作,返回数据组成新数组返回 var newArr = arr.map(function(v, index, arr){return v*2;});
filter: 对数组进行过滤,满足条件的项组成新数组返回 var newArr = arr.filter(function(v, index, arr){return v > 10;});
reduce:对数组的相邻两项进行操作 var sum = arr.reduce(function(sum, item){return sum+item;}, 0)
includes:数组是否包含某值 if(arr.includes(2)){}

函数的扩展

  • 参数默认值:双重默认值 function({x=0,y=0} = {}){...},尽量将定义了默认值的参数放于参数的尾部,方便省略

  • (function(a,b,c=1){}).length //2 返回函数的预期参数个数(不含指定了默认值的参数和rest参数)

  • rest参数:只能是尾参数

  • 扩展运算符:... 将数组转换成逗号分隔的数据序列

  • 箭头函数=>:this指向定义时的作用域(this始终指向定义时的this,而非运行时的this)

  • 函数绑定运算符(双冒号** ::**)obj::method 绑定this

  • 尾调用、尾递归调用(先做了解):尾调用-函数的最后一步是调用另一个函数,内部函数可以不用再维护外部函数的调用栈信息。

对象的扩展

  • 属性名表达式 obj['a'+'bc'] = 111; 简洁属性表示法 let obj = {x, y};
  • Object.keys() .values() .entries()
  • Null传导运算符** ?:** 判断对象是否存在,防止报错 person?.user?.p1?.age
  • 对象的扩展运算符** ...** 单纯的解构赋值可以读取对象的继承属性,扩展运算符的解构赋值只能读取对象自身的属性
var o = Object.create({ x: 1, y: 2 });//x y算继承属性
o.z = 3;

let { x, ...m } = o;
x // 1---单纯的结构赋值
m // {z: 3} --- 扩展运算中的解构赋值
  • 对象属性的遍历5种方法
for...in //返回对象自身和继承的可枚举属性
Object.keys() //返回对象自身可枚举属性
Object.getOwnPropertyNames() //返回对象自身所有属性(包括不可枚举)
Object.getOwnPropertySymbols() //返回对象自身Symbols属性
Reflect.ownKeys() //返回对象自身symbol类型和字符串类型的属性(不管是否可枚举)
方法 适用范围 描述
for...in 数组、对象 获取可枚举的实例和原型属性名(返回键名)
Object.keys() 数组、对象 返回可枚举的实例属性名组成的数组
Object.getOwnPropertyNames() 数组、对象 返回除原型属性以外的所有属性(包括不可枚举的属性)名组成的数组
for…of 可迭代对象(Array Map Set等) 返回属性值(返回键值)
Reflect.ownKeys() 对象自身的所有属性,包含symbols属性和不可枚举属性
  • object.is(p1, p2):同值判断 基本等同于全等===,不同:
    object.is(+0, -0);//false
    object.is(NaN, NaN);//true
  • Object.assign(target, source1, source2):对象合并,浅拷贝,只拷贝对象自身的属性,继承(prototype上的属性)+不可枚举的属性都不会拷贝到。
    首参不能是null、undefined,否则报错;
    处理数组时:
    Object.assign([1, 2, 3], [4, 5]) // [4, 5, 3] 按照下标进行覆盖合并
    Object.create(原型 | null,实例属性设置):只有显示的指定属性设置的enumrabe等属性才可以。属于ES5

Symbol

ES6新增原始数据类型Symbol,表示独一无二的值(对象属性名可以保证不会冲突)。Symbol给我感觉用处不是很顺畅,很别扭的感觉。

  • Symbol(str):参数表示对Symbol实例的描述,不能用new(不是对象);Symbol值不能进行隐士字符串转换,只能显示转换String(Symbol('aa')) or Symbol('aa').toString(),也不能转换成数值,但是可以进行布尔换算
  • Symbol值作为属性名时,只能用[]引用,不能用点.获取(用点默认代表字符串属性)
  • Symbol.for('aa') 是全局登记的值:每次会先搜索给定参数的值是否存在,如果已经存在,则返回,否则新建一个值返回。Symbol.keyFor(Symbol.for('aa'))返回Symbol.for()注册的值的描述。
Symbol('aa') === Symbol('aa') //false
Symbol.for('aa') === Symbol.for('aa') //true
Symbol.keyFor(Symbol.for('aa')) //'aa'
Symbol.keyFor(Symbol('aa'))//undefined
  • 定义了一些内置的Symbol值,用于指向语言内部的方法

版权声明:本文由龙竞技官网发布于龙电竞官网,转载请注明出处:ES6早先记录(较乱)