一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第24天,点击查看活动详情。
闭包
闭包是 JS 最强大的特性之一,它允许函数访问局部作用域之外的数据
闭包的使用也可能会导致性能问题。
function sayName(){
const name = 'xiaoming';
document.getElementById("btn").onclick = function (e){
e.target.value = name;
}
这个事件处理函数就是一个闭包,在 sayName 函数执行的时候创建,它能够访问到所属作用域中的 name 变量。
为了能访问 name, 会创建一个特定的作用域链。执行 sayName 函数时,一个包含了变量 name 的活动对象被创建,成为执行环境作用域中的第一个对象。
由于闭包的 [[Scope]] 属性包含了与执行环境作用域相同的对象的引用,因此会产生副作用。通常来说函数的活动对象会随着执行环境一同销毁,如果引入闭包由于引用仍然存在于闭包的[[Scope]] 属性中,因此激活对象无法被销毁。这意味着脚本中闭包与非闭包函数相比需要更多的内存开销。
当闭包执行时,会创建一个执行环境,它的作用域与属性[[Scope]] 中所引用的两个相同的作用域链对象一起被初始化,然后一个活动对象为闭包自身所创建。
在频繁访问跨作用域的标识符时,每次访问都会带来性能损失。在脚本编程中最好小心地使用闭包,它同时关系到内存和执行速度。
对象
大部分 JS 代码是面向对象风格编写的,无论是通过创建自定义对象还是使用内置对象。然而这会导致非常频繁的访问对象成员。
对象成员包括属性和方法,在 JS 中 一个被命名的对象成员能包含任何数据类型。既然函数也是一个对象那么对象成员除了传统的数据类型外还可以包含函数。当一个被命名的成员引用了一个函数,该成员被称为"方法",引用了非数据类型就称为"属性"。
由于对象成员可能包含其他成员,例如不太常见写法:window.location.href。每次遇到点操作符,嵌套成员会导致 JS 搜索引擎搜索所有处对象成员。对象成员嵌套得越深读取速度就越慢。执行 location.herf 总比 window.location.href 要快,后者也比 window.location.herf.toString() 要快。如果这些属性不是对象的实例属性,那么成员解析还需要搜索原型链,这会花更多时间。
原型
JS 中的对象是基于原型的,原型是其他对象的基础。
原型定义并实现了一个新创建的对象所必须包含的成员列表。这一概念完全不同于传统面向对象编程语言的"类","类"定义了创建新对象的过程,而原型对象为所有对象实例共享,因此这些实例也共享了原型对象的成员。
对象通过一个内部属性绑定到它的原型,一旦创建了一个内置对象的实例,他们就会自动拥有一个Object 实例作为原型。
因此对象可以有两种成员类型: 实例成员和原型成员。实例成员直接存于对象实例中,原型成员则从对象原型继承而来。
原型链
象的原型决定了实例的类型
默认情况下所有对象都是 Object的实例,并继承了所有的基本方法。你可以定义并使用构造函数来创建另一种类型的原型。
function Dog(name,age){
this.name = name;
this.age = age;
}
Dog.prototype.say = function(){
console.log(this.name);
}
const WangCai = new Dog('旺财', 3);
WangCai.say();
使用构造函数 Dog 来创建一个新的 Dog 实例,实例 Wangcai 的原型(__proto__)是 Dog.prototype, 而 Dog.prototype 的原型是 Object。在原型的创建过程中继承了原型链的所有成员。