webveuje/js/kejian/zuoyongyu.md
2021-03-23 10:58:10 +08:00

193 lines
4.5 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 作用域
### 练习
```
var color = "blue";
function changeColor() {
let anotherColor = "red";
function swapColors() {
let tempColor = anotherColor;
anotherColor = color;
color = tempColor;
// 这里可以访问color、anotherColor和tempColor
}
// 这里可以访问color和anotherColor但访问不到tempColor
swapColors();
}
// 这里只能访问color
changeColor();
```
分析上面的作用域链:
<details>
<summary>展开查看</summary>
* window
* color
* changecolor()
* anothercolor
* swapcolors()
* tempcolor
</details>
### 垃圾回收机制
优化内存占用的最佳手段就是保证在执行代码时只保存必要的数据。如果数据不再必要那么把它设置为null ,从而释放其引用。这也可以叫作解除引用 。这个建议最适合全局变量和全局对象的属性。
局部变量在超出作用域后会被自动解除引用
即 全局变量(GO) 不会被销毁,但是局部变量(AO)函数执行完后会被销毁
也可以手动销毁变量即赋值为null
如下面的例子所示:
```
function createPerson(name){
let localPerson = new Object();
localPerson.name = name;
return localPerson;
}
let globalPerson = createPerson("Nicholas");
// 解除globalPerson对值的引用
//globalPerson = null;
```
解析:
<details>
<summary>展开查看</summary>
在上面的代码中变量globalPerson 保存着createPerson() 函数调用返回的值。在createPerson() 内部localPerson 创建了一个对象并给它添加了一个name 属性。然后localPerson 作为函数值被返回并被赋值给globalPerson 。localPerson 在createPerson() 执行完成超出上下文后会自动被解除引用不需要显式处理。但globalPerson 是一个全局变量,应该在不再需要时手动解除其引用,最后一行就是这么做的。
不过要注意,解除对一个值的引用并不会自动导致相关内存被回收。解除引用的关键在于确保相关的值已经不在上下文里了,因此它在下次垃圾回收时会被回收。
</details>
### 闭包
闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式,就是在一个函数内部创建另一个函数。
```
function a() {
var x = 0;
return function(y) {
x = x + y;
// return x;
console.log(x);
}
}
var b = a();
b(1); //1
b(1); //2
```
练习1
```
function fn() {
var max = 10;
return function bar(x) {
if (x > max) {
console.log(x);
}
};
}
var f1 = fn();
f1(15);
```
练习2
```
function init() {
var name = "Mozilla"; // name 是一个被 init 创建的局部变量
function displayName() { // displayName() 是内部函数,一个闭包
alert(name); // 使用了父函数中声明的变量
}
displayName();
}
init();
```
<details>
init() 创建了一个局部变量 name 和一个名为 displayName() 的函数。displayName() 是定义在 init() 里的内部函数,并且仅在 init() 函数体内可用。请注意displayName() 没有自己的局部变量。然而,因为它可以访问到外部函数的变量,所以 displayName() 可以使用父函数 init() 中声明的变量 name 。
</details>
hrefs:
https://blog.csdn.net/weixin_43586120/article/details/89456183
http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html
```
(function() {
var m = 0;
function getM() { return m; }
function seta(val) { m = val; }
window.g = getM;
window.f = seta;
})();
f(100);
console.info(g());
```
<hr/>
```
function fn(){
var arr = [];
for(var i = 0;i < 5;i ++){
arr[i] = (function(i){
return function (){
return i;
};
})(i);
}
return arr;
}
// 立即执行函数 每调用一次完成后就自动销毁
var list = fn();
for(var i = 0,len = list.length;i < len ; i ++){
console.log(list[i]());
} //0 1 2 3 4
```
```
function fn(){
var arr = [];
for(var i = 0;i < 5;i ++){
arr[i] = function(){
return i;
}
}
return arr;
}
var list = fn();
for(var i = 0,len = list.length;i < len ; i ++){
console.log(list[i]());
} //5 5 5 5 5
```
```
var lis = document.getElementsByTagName("li");
for(var i=0;i<lis.length;i++){
(function(i){
lis[i].onclick = function(){
console.log(i);
};
})(i); //事件处理函数中闭包的写法
}
```
```
var li = document.getElementsByTagName( "li" );
   for ( var i = 0; i < li.length; i++) {
li[i].addEventListener( "click" , function () {
console.log(i);
})
}
```