作用域精解
eg:函数累加器
可以做缓存(存储结构)
eg:eater
可以实现封装,属性私有化
eg:Person();
模块化开发,防止污染全局变量
- [[scope]]:每个javascript函数都是一个对象,对象中有些属性我们可以访问,但有些不可以,这些属性仅供javascript引擎存取,[[scope]]就是其中一个。[[scope]]指的就是我们所说的作用域,其中存储了运行期上下文的集合。
- 作用域链:[[scope]]中所存储的执行期上下文对象的集合,这个集合呈链式链接,我们把这种链式链接叫做作用域链。
- 运行期上下文:当函数执行时,会创建一个称为执行期上下文的内部对象。一个执行期上下文定义了一个函数执行时的环境,函数每次执行时对应的执行上下文都是独一无二的,所以多次调用一个函数会导致创建多个执行上下文,当函数执行完毕,它所产生的执行上下文被销毁。
- 查找变量:从作用域链的顶端依次向下查找。
function a() {
function b() {
var b = 234;
}
var a =123;
b();
}
var glob = 100;
a();
b | scope chain作用域链 | |||
[[scope]] | 作用域链 | 0 | b的AO | |
1 | a是AO | |||
2 | GO |
闭包
当内部函数被保存到外部时,将会生成闭包。闭包会导致原有作用域链不释放,造成内存泄漏。
function a() {
function b() {
var bbb = 234;
document.window(aaa);
}
var aaaa =123;
return b;
}
var glob = 100;
var demo = a();
demo();
//内部的函数通过return保存到外部一定形成闭包
function a() {
var num = 100;
function b() {
num ++;
console.log(num);
}
return b;
}
var demo = a();
demo();//101
demo();//102
//b被返回时,还没有被执行
闭包的作用
实现公有变量eg:函数累加器
可以做缓存(存储结构)
eg:eater
可以实现封装,属性私有化
eg:Person();
模块化开发,防止污染全局变量
//函数累加器
function add() {
var count = 0;
function demo() {
count ++;
console.log(count);
}
return demo;
}
var counter = add();
counter();
counter();
counter();
//一对二的闭包
function test() {
var num = 100;
function a() {
num ++;
console.log(num);
}
function b() {
num --;
console.log(num);
}
return [a, b];
}
var myArr = test();
myArr[0]();//101
myArr[1]();//100
//存储结构
function eater() {
var food = "";
var obj = {
eat : function () {
console.log("i am eating " + food);
food = "";
},
push : function (myFood) {
food = myFood;
}
}
return obj;
}
var eater1 = eater();
eater1.push('banana');
eater1.eat();//banana
function test() {
var food = "apple";
var obj = {
eatFood : function () {
if(food != "") {
console.log("i am eating " + food);
food = "";
}else{
console.log("empty!")
}
},
pushFood : function (myFood) {
food = myFood;
}
}
return obj;
}
var person = test();
person.eatFood();//i am eating apple
person.eatFood();//empty!
person.pushFood('banana');
person.eatFood();//banana
//多个函数被保存到外面形成闭包,用的是同一个food
立即执行函数
定义:此类函数没有声明,在一次执行过后即释放。适合做初始化工作。
//针对初始化功能的函数
(function (){
var a = 123;
var b = 234;
console.log(a + b);
}())
(function (a, b, c){
console.log(a + b + c * 2);
}(1, 2, 3))
//可以加参数
var num = (function (a, b, c){
var d = a + b + c * 2 - 2;
return d;
}(1, 2, 3))
//返回值
//(function (){}()); W3C建议第一种
//(function (){})();
//只有表达式才能被执行符号执行
//能被执行符号执行的表达式,名字就会被忽略
var test = function () {
console.log('a');
}();
//执行完之后再打印test,显示undefined
+ function test() {
console.log('a');
}();
//加正负号叹号就变成表达式,放弃表达式名字
//加括号把函数声明变成表达式,放弃名字,就变成立即执行函数(function (){}());
function test(a, b, c, d) {
console.log(a + b + c + d);
}(1, 2, 3, 4);
//系统不报错也不执行,系统将最后的括号和上面的函数看成是2个东西
function test() {
var arr = [];
for(var i = 0; i < 10; i++) {
(function(j) {
arr[j] = function () {
console.log(j);
}
}(i));
}
return arr;
}
var myArr = test();
for(var j = 0; j < 10; j++) {
myArr[j]();
}
//十个函数对应十个函数
练习
1.写一个方法,求一个字符串的字节长度。
function retByteslen(target) {
var count = target.length;
for(var i = 0; i < target.length; i++) {
if(target.charCodeAt(i) > 255) {
count ++;
}
}
console.log(count);
}
retByteslen('sadfasdfs对方是否');
//再简化
function retByteslen(target) {
var count,
len;
count = len = target.length;
for(var i = 0; i < len; i++) {
if(target.charCodeAt(i) > 255) {
count ++;
}
}
console.log(count);
}
retByteslen('sadfasdfs对方是否');
//逗号操作符,先看前面的表达式先计算,再看后面的表达式计算,把后面的表达式计算结果返回数据
var a = (1 - 1, 1 + 1);//2
2.写出下面程序的执行结果:
var f = (
function f() {
return "1";
},
function g() {
return 2;
}
)();
typeof f;//number
3.写出下面程序的执行结果:
var x = 1;
if(function f() {}) {
x += typeof(f);
}
x;//1undefined
//括号里面的函数变成了表达式,f就没有了,未经声明的f就是个字符串类型的undefined