查缺补漏
1. defer和async属性
defer:脚本执行时不会影响页面的构造,脚本将延迟在遇到</html>
后再执行,并且先于DOMContentLoaded
事件执行
async:标记的脚本并不保证按照指定的先后顺序执行,并且不让页面等待脚本下载和执行,从而异步加载页面的其他内容,会在load
事件前执行,可能会在DOMContentLoaded
前或后执行
2. 严格模式
use strict
开启严格模式,可以在函数内部声明,指明函数在严格模式下执行
3. typeof
未声明的变量同样返回undefined
// 未声明
// var age
typeof age // undefined
4. for in
可以用来枚举对象的属性
- 循环顺序不确定
- 建议使用之前先确定对象是否为null或者undefined(es5前会报错)
5. with
将当前作用域设置到一个特定的对象之中
with(location) {
var qs = search.substring(1)
var hostName = hostname
var url = href
}
6. 没有重载
没有像java一样的重载方法来对同样的方法名写出不同的处理
如果同一个函数名的函数被定义多次,只取最后一个定义的函数
7. 垃圾回收机制
-
标记清除:对所有变量进行标记,再对局部变量和函数内引用的变量去除标记,最后将有标记的变量作为准备清除的变量
-
引用计数:跟踪被引用的次数,引用次数为0时在下一次垃圾回收时清除,存在
循环引用
的弊端,导致计数不能为0,无法被清除 -
管理内存:在对象不被使用时手动赋值为
null
随着 IE7 的发布,其 JavaScript 引擎的垃圾收集例程改变了工作方式:触发垃圾收集的变量分配、
字面量和(或)数组元素的临界值被调整为动态修正。 IE7 中的各项临界值在初始时与 IE6 相等。如果
垃圾收集例程回收的内存分配量低于 15%,则变量、字面量和(或)数组元素的临界值就会加倍。如果
例程回收了 85%的内存分配量,则将各种临界值重置回默认值。这一看似简单的调整,极大地提升了 IE
在运行包含大量 JavaScript 的页面时的性能。
8. 数组
- 最多包含4, 294, 967, 295项
9. 数组ES5方法
- every()
- filter()
- forEach()
- map()
- some()
- reduce()
- reduceRight()
- indexOf()
- lastIndexOf()
- push()
- pop()
- shift()
- unshift()
- splice()
- slice()
- concat()
- reverse()
- sort()
10. Date
- Date.now()
- Date.UTC()
- Date.parse()
11. Function
- 函数声明创建的函数会进行提升,在任何地方都可以使用
- 函数表达式创建的函数只有在解析到所在代码行后才可以正常使用
- arguments拥有属性callee指向拥有arguments的函数
12. 基本包装类型
- 引用类型与基本包装类型的主要区别就是对象的生存期。使用 new 操作符创建的引用类型的实例,
在执行流离开当前作用域之前都一直保存在内存中。而自动创建的基本包装类型的对象,则只存在于一
行代码的执行瞬间,然后立即被销毁。这意味着我们不能在运行时为基本类型值添加属性和方法。
13. 转型函数
- 使用
new
调用的基本包装类型的构造函数,和直接调用的转型函数不一样
typeof new Number(1) // 'object'
typeof Number(1) // 'number'
14. Object.defineProperty()
- IE8是第一个实现
Object.defineProperty()
的浏览器版本,但限制较多,只能在DOM对象上使用(Vue只兼容到IE9原因?)实现不够彻底 Object.defineProperty()
Object.defineProperties()
-
Object.getOwnPropertyDescriptor
读取属性的描述符
const obj = { a: 1 }
const descriptor = Object.getOwnPropertyDescriptor(obj, 'a')
15. 构造函数
new 的作用
1)创建一个新对象
2)将构造函数的作用域赋给新对象(this)
3)执行构造函数中的代码
4)返回这个新对象
16. 原型模式
- 创建了自定义的构造函数之后,其原型对象默认只会取得
constructor
属性,其他方法都是从Object
继承而来的 - 创建的新实例内部包含一个指针指向原型对象,叫做
[[Prototype]]
,在Firefox
,Safari
,Chrome
中实现了__proto__
属性能够访问到原型对象 - 对于实例和原型对象的关系,可以使用
isPrototypeOf()
来检测
function Person () {
this.name = 'John'
}
const person = new Person()
Person.prototype.isPrototypeOf(person) // true
Object.prototype.isPrototypeOf(person) // true
// es5 新增 Object.getPrototypeOf() 返回 [[Prototype]] 的值
// 兼容性IE9+
Object.getPrototypeOf(person) == Person.prototype // true
Object.getPrototypeOf(person).name // John
- 遍历所有可以/不可以枚举的实例属性,使用
Object.getOwnPropertyNames()
-
实例中的指针仅指向原型,而不指向构造函数,也就是说实例访问原型是直接访问原型的指针,而不是通过访问构造函数的
prototype
属性间接访问的
function Person () {}
const person = new Person()
Person.prototype = {
constructor: Person,
sayName: function() {
console.log('name')
}
}
person.sayName() // Error

17. 原型链继承
17.1 组合继承(伪经典继承)
- 借用构造函数来实现对实例属性的继承,以及利用原型链来继承原型对象的属性
function Parent (name) {
this.name = name
this.color = ['red', 'blue', 'green']
}
Parent.prototype.sayName = function () {
console.log(this.name)
}
function Child (name, age) {
Parent.call(this, name)
this.age = age
}
Child.prototype = new Parent()
Child.prototype.constructor = Child
Child.prototype.sayAge = function () {
console.log(this.age)
}
var instance1 = new Child('a', 21)
instance1.color.push('black')
console.log(instance1.color) // red,blue,green,black
instance1.sayName() // a
instance1.sayAge() // 21
var instance2 = new Child('b', 22)
console.log(instance2.color) // red,blue,green
instance2.sayName() // b
instance2.sayAge() // 22
17.2 原型式继承
function object(o) {
function F() {}
F.prototype = o
return new F()
}
- 借助原型可以基于已有的对象创建新的对象
- 必须有一个对象可以作为另一个对象的基础
- es5通过新增
Object.create()
方法规范化了原型式继承
// 兼容性IE9+
var person = {
name: 'a'
}
var anotherPerson = Object.create(person, {
name: {
value: "Greg"
}
})
17.3 寄生式继承
- 思路类似于寄生构造函数和工厂模式,封装一个函数实现
- 类似于构造函数模式,不能够做到函数复用,每次传入都需要生成新的函数
function createAnother(o) {
var clone = object(o)
clone.sayHi = function() {
console.log('hi')
}
return clone
}
17.4 寄生组合式继承
- 解决了组合式继承方法,Child的原型对象上会多出Parent的实例属性,而Child的实例又拥有相同的属性覆盖了原型对象上属性的问题
- 通过借用构造函数来继承属性,通过原型链的混成形式来继承方法
function Parent(name) {
this.name = name
this.color = ['red', 'blue', 'green']
}
Praent.prototype.sayName = function() {
console.log(this.name)
}
function Child(name, age) {
Parent.call(this, name)
this.age = age
}
Child.prototype = Object.create(Parent.prototype, {
constructor: {
value: Child,
writable: true,
enumerable: false,
configurable: true
}
})
const child = new Child()
child.sayName()
18. 创建对象(类)
三种模式
- 工厂模式:使用最基本的方法创建,然后返回这个对象
- 构造函数模式:使用了
this
关键字去定义属性和方法,缺点是不能复用,比如函数 - 原型模式:使用
prototype
来定义共享的属性或者方法
19. 函数递归
- 存在的问题:如果说函数内递归调用时,函数名称的变量值发生了改变,就会导致出错,对外界的依赖度太大
-
arguments.callee
是指向正在执行函数的指针,严格模式下会报错
function factorial(num) {
if (num <= 1) {
return 1
} else {
return num * factorial(num - 1)
}
}
// 尝试下面的情况
var anotherFactorial = factorial
factorial = null
anotherFactorial(4) // 出错
// 解决办法,使用arguments.callee
function factorial(num) {
num <= 1 ? return 1 : return num * arguments.callee(num - 1)
}
// 严格模式下解决办法
var factorial = (function f() {
num <= 1 ? return 1 : return num * f(num - 1)
})
20. BOM
- 获取屏幕宽高标准的顺序
const screenW = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth
const screenH = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight
-
document.location
和window.location
引用的是同一个对象 - BOM的核心对象是
window
,在浏览器中,是ECMAScript规定的Global
对象 - 全局定义的变量会自动成为window对象的属性,但是全局变量和window上定义的属性进行删除时有一点不同
var age = 20
window.color = 'red'
// IE9以下都报错
delete window.age // return false
delete window.color // return true
21. DOM

操作api:
appendChild
insertBefore
replaceChild
removeChild
-
cloneNode
(不会复制添加到DOM节点中的js事件/属性) normalize
常见节点
- document.documentElement
- document.body
<body>
<ul>
<li>123</li>
</ul>
</body>
- 如上结构打印
document.body.childNodes
长度为3,第1和第3个都是Text
的Node
,为换行符,只有中间第2个是ul
标签
常见属性
document.title
document.URL
document.referer
document.domain
获取Node
document.getElementById
document.getElementsByTagName
document.getElementsByClassName
document.getElementsByName
-
children
属性,HTMLCollection
实例 - 返回的是
HTMLCollection
,有namedItem()
方法,可以筛选name
属性
设置属性
getAttribute
setAttribute
removeAttribute
理解DOM
- DOM操作是js中开销最大的部分,因为NodeList都是动态的,所以每次访问都会进行一次查询,为了提升性能,尽量减少DOM的操作
22. DOM
扩展
- 主要增加了选择符API(Selectors API)和HTML5的扩展
Selectors API
Selectors API Level 1
- 兼容性IE8+
querySelector
querySelectorAll
-
querySelectorAll
的返回值实际上相当于匹配元素的一个快照,而不是对文档进行动态的查询
Selectors API Level 2
-
matchesSelector
方法,接收css
选择符,如果匹配返回true
- 不同浏览器的实现不同,表现为不同的前缀,不建议使用

元素遍历
由于IE9及之前对于换行的标签不返回文本节点,而其他浏览器会返回一个内容为换行符的文本节点,为了弥补差异
-
childElementCount
: 返回子元素的个数 -
firstElementChild
: 第一个子元素 -
lastElementChild
: 最后一个子元素 -
previousElementSibling
: 前一个同辈元素 -
nextElementSibling
: 后一个同辈元素 - 兼容IE9+
HTML5
-
getElementsByClassName
是H5添加的 -
classList
属性,提供了标签类名的list,是DOMTokenList
的实例 classList.add()
classList.contains()
classList.remove()
classList.toggle()
- 支持的浏览器Firefox 3.6+ 和 Chrome(遗憾脸)
- 这些内置类型其实继承自
Object

提高无障碍性的焦点管理
- 通过
document.activeElement
属性来获取当前获得焦点的元素,通常是用户的输入,或者调用了focus()
方法,文档加载期间默认指向body
-
document.hasFocus()
来判断是否有焦点 - 兼容性不错
*HTMLDocument
(作用不大)
-
document.readyState
:loading
/complete
- 兼容模式:
document.compatMode
-
head
:document.head
相当于body
和document.body
-
document.charset
/document.defaultCharset
: 默认UTF-16
自定义属性
- 必须添加前缀
data-
,通过元素的dataset
来访问
<p data-status="1"></p>
<script>
document.getElementsByTagName('p')[0].dataset.status // 1
</script>
其他
-
innerHTML
只插入script标签后并不会运行,因为是一个“无作用域”的元素,如果需要使script生效必须插入一个“有作用域”的元素(空div不行) -
insertAdjacentHTML
: 插入html,在不同的位置document.insertAdjacentHTML('位置', html)
-
beforebegin
: 在当前元素的前面插入一个元素作为它的previousSibling
-
afterbegin
: 在当前元素里面插入一个元素作为第一个子元素 -
beforeend
: 在当前元素里面插入一个元素作为最后一个子元素 -
afterend
: 在当前元素的后面插入一个元素作为它的nextSibling
-
element.scrollIntoView()
,滚动到元素所在位置
性能问题
- 使用上述的API如
innerHTML
进行DOM操作时,如果将带有事件处理的元素或引用了js对象的元素删除时,其绑定的内容并未删除,将导致内存的占用
网友评论