本文主要内容是笔者学js以来记录的一些问题以及一些使用技巧。大部分都可以在网路上找到,因为时过久远,不保证所有链接都有效.
生成包含n个元素的数组
|
|
ES6 let变量暂时性死区域DTZ
参见阮一峰的ES6教程
ES6明确规定,如果区块中存在let和const命令,这个区块对于这些命令声明的变量,从一开始就形成封闭作用域。凡是在声明之前使用这些变量就会报错.
JS引擎在使用变量时,他们的得生命周期包含三个部分:声明阶段、初始化阶段、赋值阶段。
比如,以下代码就会报错
相关联的有那个变量声明提升以及匿名函数声明提前.
比如:
注意网上关于let/const是否存在hoisting是有争议的,主要争议来源MDN上明确说let不支持hoisting,而Ecmscript里提到过let/const hoisting这个关键词。我个人理解是hoisting只是一个概念,而且本身并没有准确的定义,其实只要知道js引擎在处理var和let/const的区别就可以了,没必要把这个概念强加在let上。
参考文章:【译】JavaScript 变量的生命周期:为什么 let 不存在变量提升
引用传值问题
之前对此我也很疑惑,后来发现只要这样理解就可以了.
结果可能跟你想象的不太一样!我的理解是在解释器在进行编译时,执行a.x = a = { n: 2 }时,a均是指向的初始地址{ n: 1 }所处的地址空间,也即b指向的地址.
a = { n: 2 }所做的操作即是a重新指向另外一个存储{ n: 2 }的地址.前面已经说过了,a.x指向的仍旧是原来的地址.最后结果就是:
再看一例子说明一下:
可以理解为函数调用过程let tmp = 实参,然后对tmp进行计算
函数式编程
关于函子Functor:
famp: (a->b) -> [a] -> [b]
famp参数(a->b)函数映射,[a]函子值,返回[b]函子值
简单讲就是函数可以作用域函子值上,结果就是该函数在函子上的映射结果
关于函数式不是太理解,可能以后会学习Haskell加深理解
- [推荐]参见js函数式编程指南在线阅读
- 函数式编程中Functor和Monad的形象解释
- Functor, Applicative以及Monad的图片阐释
关于void
|
|
注意expression必须是一个整体,要括起来,和undefined
的区别是会计算expression,若你看到有的人代码写void(0)
也好,void 0
也好,void 666
也好,不要惊讶,因为它和undefined
等价!
注意:在ES5之前,undefined可以被改写!不过好在ES6之后,undefined只可读,但是局部变量依然可以对之改写!
所以说用void 0
来赋值undefined
是一个好习惯!
关于const的问题
问题:const修饰的变量可以改吗?
const修饰的变量指向地址不可变,但是里面存储的值是可以变动的!
值得注意的是const修饰的变量需要初始化.
箭头函数的一个作用
箭头函数可以节省代码, => 替代function还是比较简洁的。值得注意的是箭头函数绑定的是父作用域,所以不用担心再es5中this奇怪指向,基本上可以少写一些bind之类的函数.
顺带了解一下ES5里绑定代码块作用域关键字with(虽然跟goto一样因为容易被滥用,所以基本上被禁用的)。但是有些时候还是会有用的,比如vue编译模板就用到了with来绑定模板作用域。所以你在vue组件里prop可以直接写:attr="attr"
而不用写成:attr="this.attr"
。
Object的一个问题
|
|
关于原型链
在ES5注解里4.2.1节有一段描述Object中prototype:
Every object created by a constructor has an implicit reference (called the object’s prototype) to the value of its constructor’s “prototype” property. Furthermore, a prototype may have a non-null implicit reference to its prototype, and so on; this is called the prototype chain. When a reference is made to a property in an object, that reference is to the property of that name in the first object in the prototype chain that contains a property of that name. In other words, first the object mentioned directly is examined for such a property; if that object contains the named property, that is the property to which the reference refers; if that object does not contain the named property, the prototype for that object is examined next; and so on.
里面的prototype是显式引用,__proto__
是隐式引用(部分浏览器支持__proto__
),该图指明了这样一个关系:
ES5里的arguments, caller和callee
注意: callee和caller以及arguments已在ES6里废弃
关于arguments.callee指向当前函数 [function].caller指向调用该函数的外层函数
在ES6中虽然废弃了arguments,可以使用解构来接收参数:
Yoda尤达表达式
其实就是我们常见的那种条件判断:
Yoda表达式的优点:
- 可以避免一些问题.比如
if (num = 42)
无意识会将42赋值给num,改变了num的值,而采用Yoda表达式if (42 = num)
则编译错误. - 解决了上例中不安全的空指针隐患.
当然Yoda条件也有一些缺点:
- 可读性差.(批评者认为弊大于利!)
而在另外一些语言里比如Python,Swif禁止条件判断力包含赋值语句,通过赋值语句不返回值来避免这个问题.一般认为Yoda表示法是为了变通一些语言的设计,而这些设计可能会带来一些小问题,比如用=
表示赋值,==
表示比较,开发者容易将比较写成=
,由此容易引发一些问题。所以网路上一般不推荐使用Yoda表示法.综上在js里,Yoda表示法还是有用的!值得注意的是,一个良好的编程习惯应当尽量不要在条件判断里写赋值语句!
详细请参考以下链接:
对象深拷贝
常规的做法就是递归遍历每个属性,然后对每个属性进行拷贝。以前我以为只要Array、Function、Object、基本值类型这几类拷贝就行了,然而还是too young too naive!
包括jquery和lodash(undersocre)在内的一些库对于深拷贝的实现都有很多行代码。如jquery的有六十来行(当然jQuery也没有处理好循环引用的问题),而lodash有上百行,可见水不浅!
当然如果对象里只包含Number, String, Boolean, Array, null等扁平对象,即那些能够被 json 直接表示的数据结构,则可以使用JSON.parse(JSON.stringify(obj))
来进行深拷贝
参考链接:深入剖析 JavaScript的深拷贝
其它
|
|
/ 后面加上的 /
其他相关参考文章
浏览器控件重绘问题概述浏览器重排、重绘、渲染机制
要点:因为浏览器优化,会将某些操作合并到一起,如果属于不同操作都可能触发浏览器重回,所以尽量把同类型操作写一起!