JavaScript(ECMAScript + DOM + BOM)
写在最前面,希望大家能用心体会。
对于前端开发来说,JavaScript语言基础非常重要,推荐《JavaScript权威指南》这本书,至少要看完一遍。
由于JavaScript语言属于弱类型语言,编译阶段能暴露出的问题有限,因此好的编码风格和习惯真的很重要!
一些你必须要知道的概念
- 在JS中,Infinity 属于特殊量,表示无穷大
- 当算术运算溢出或被0整除时,不会报错,而是返回Infinity(无穷大),注意:0/0 = NaN
- 当发生下溢时,也不会报错,而是返回0。下溢:指的是当一个数无限接近于0,且超出了JavaScript所能表示的最小值时的情况
- JavaScript中语句结尾的分号并不是直接自动补齐的,而是结合下一行代码分析后加上去的,如果下一行代码是‘[’或者’(’ 括号开头,则容易解析成数组或者函数,导致出错。切记切记!
- arr[i++]*=2 不等于 arr[i++] = arr[i++]*2
- this是关键字,不是变量,但可以将this赋值给一个已声明的变量
- “use strict”; 开启严格模式
类型转换
JavaScript中,类型会自动转换成当前语句所需的类型,具体转换情况如下:
值 转换为: | 字符串 | 数字 | 布尔值 | 对象 |
---|---|---|---|---|
undefined | “undefined” | NaN | false | throw TypeError |
null | “null” | 0 | false | throw TypeError |
true | “true” | 1 | true | new Boolean(true) |
false | “false” | 0 | false | new Boolean(false) |
“” | “” | 0 | false | new String("") |
“1.2” | “1.2” | 1.2 | true | new String(“1.2”) |
“one” | “one” | NaN | true | new String(“one”) |
0 | “0” | 0 | false | new Number(0) |
-0 | “0” | -0 | false | new Number(-0) |
NaN | “NaN” | NaN | false | new Number(NaN) |
Infinity | “Infinity” | Infinity | true | new Number(Infinity) |
-Infinity | “-Infinity” | -Infinity | true | new Number(-Infinity) |
1 | “1” | 1 | true | new Number(1) |
{} | “[object Object]” | –待确认– | true | |
[] | “” | 0 | true | |
[9] | “[9]”, | 9 | true | |
[‘a’] | “a” | NaN | true | |
function(){} | – | NaN | true |
// 注意这两种类型转换情况,圆括号括起来的在JS中会被解释成表达式,
{} + 1 = 1 // 0 + 1 = 1
({} + 1) = "[object Object]1" // '[object Object]' + '1' = '[object Object]1'
变量作用域链
- var: ES6之前的所有变量声明标识符,会出现“变量提升”现象,即在变量声明之前使用该变量,不会报错。
- let:ES6的新特性,不会发生“变量提升”现象。仅在let声明的代码块中起作用,且在声明之前使用会报错,提示该变量还未声明。
- const:声明一个不可改变的常量,因为后续不允许改变,因此就必须在声明时初始化一个值。注意,当声明一个对象常量时,不变的是对象的指针,而不是对象里的属性。
推荐看下这篇文章,列举了几个典型的例子帮助我们更好的理解变量的作用域
https://www.cnblogs.com/liutianzeng/p/10429833.html
// 注意let作用域的“暂时性死区”对typeof的影响
typeof x; //"undefined"
typeof x; let x = 0; // Uncaught ReferenceError: x is not defined
// 注意for循环小括号和大括号的作用域问题
for(var ss=0;ss<33;ss++);
console.log(ss); // 33
for(let gg=0;gg<33;gg++);
console.log(gg); // Uncaught ReferenceError: gg is not defined
// var 变量提升演示
console.log(1,qq); // 1 undefined
{
console.log(2,qq); // 2 undefined
var qq = 77;
console.log(3,qq); // 3 77
}
console.log(4,qq); // 4 77
对象原型链
Object.prototype 获取对原型对象的引用
// 创建一个对象,参数为原型对象,null表示没有原型对象
Object.create(null|{}|{x:123})
特殊的this
- 在函数外:this指向当前全局对象(浏览器中全局对象为当前窗口window)
- 在函数中:this指向调用当前函数的对象
- 在函数中的严格模式下:this 是 undefined
- 在HTML标签事件中:this指向当前的dom元素对象
- call() 和 apply() 方法可以改变this的指向
- 在setTimeout()函数中,this的指向依然遵循上述规则,只是要理解setTimeout的实现机制
// 先定义一个函数
function f(){let x = 'f-inner'; console.log('x=>' + this.x)}
// 再定义一个对象
let obj = {x: 'f-obj', f: f}
// 再定义一个全局变量x
this.x = 'f-global'
// 测试this指向
f() // x=>f-global
obj.f() // x=>f-obj
setTimeout(obj.f, 200) // x=>f-global
setTimeout('obj.f()', 200) // x=>f-obj
setInterval 与 setTimeout 一样,就不再演示了
== 比较时的隐式转换
记住5大规则
- NaN与任何类型比较都为false,包括它自己
- Boolean与其他类型比较,会先转为Number
- String与Number比较,会优先将String转换为Number
- 引用类型与原始类型比较,会先执行引用类型的valueOf,然后toString,转换成String类型再进行比较(如果toString返回的不是原始类型,则会报错)
- null==undefined // true 与其他值比较都返回false
[] == ![] // true
[] == [] // false
// 通过隐式转换,可以解释下面的情况
let a = [1,2,3]
a==1&&a==2&&a==3 // true
Object.is()
Object.is = function(a, b){
if(a===b){
// +0 !== -0
return a!==0 || 1/a !==1/b
}else{
// NaN == NaN
return a!==a
}
}
闭包
JS中很强大的一个功能,原理就是利用函数可以调用声明函数所在位置以及上级的变量
- 隔离环境,防止变量污染
- 定义私有变量,可已在多处地方访问到同一变量而不受干扰(解决经典的计数器困境)
- 为模块化提供解决方案
// 创建一个闭包函数
let add = (function(){
let counter = 0
return function(){
return counter++
}
}())
// 调用闭包函数add,此时只有add()函数能改变counter这个变量值,并且在任何地方调用add()函数,counter这个变量都是共享的
add() // 0
add() // 1
add() // 2
- 闭包中共享变量循环引用,会导致内存泄漏,无法正常触发垃圾回收
JavaScript 内存泄露的4种方式及如何避免
高级函数
- partial (简化常用函数的参数,更方便的使用,使用技术apply、array.concat)
- memorize(常用的缓存处理方法,通过闭包建立共享缓存)
ES6 新特性
- let const
- 解构
- 箭头函数