js高级程序设计读书笔记

1. js介绍

js是为了实现网络交互而是设计的脚本语言,有以下三部分组成

  • ECMAScript,由ECMA-262定义,提供核心功能,其实web浏览器是其宿主环境之一,组成部分:语法,类型,语句,关键字,保留字,操作符,对象
  • 文档对象模型(DOM),提供访问和操作页面的接口和方法,DOM把整个页面映射为一个多层节点结构,根据其提供的API,开发人员可自如增删改换节点。
    DOM1=DOM Core(映射文档结构) + DOM html(对html的对象和方法)
    DOM2新模块:
  1. DOM视图(DOM views),跟踪不同文档的视图接口
    2 .DOM事件(DOM Events),定义事件和时间处理的接口
  2. DOM样式(DOM style),基于CSS为元素应用样式接口
  3. DOM遍历和范围(DOM Traversal and Range),遍历和操作文档树的接口
    DOM3新模块:
    .DOM验证(DOM Validation)文档验证方法并支持XML1.0规范
  • 浏览器对象模型(BOM)
    根本来说,处理浏览器的窗口和框架,但人们习惯将一下扩展也算成BOM的一部分
    1.弹出新浏览器窗口
    2.移动 关闭 缩放浏览器
  1. 提供浏览器详细信息的navigator对象
    4.浏览器所加载页面的详细信息location对象
    5.用户显示器分辨率信息screen对象
    6.对cookie的支持
    7.自定义对象

2.在html中使用js

通过,以免造成误解
2.嵌入式
一般放在body后面,这样在解析JS语言之前,页面内容已经完全展示在浏览器上,不要出现浏览器明显延迟的问题

3.基本概念

  • 标识符
    驼峰大小写格式,如firstStudent
  • 注释
    //单行
    /*
    *多行
    */
  • 严格模式
    "use strict"
  • 变量
    松散类型
var message; //没初始化,因此是undefined

不使用var标识符的话就是全局变量

  • 类型typeof
    undefined,boolean, string,object,number,function
  • number:
    Infinity(正无穷)
    -Infinity(负无穷)
    isFinite()判断是否有穷
    NaN非数值isNaN()判断是否为数值
    将非数值转化为数值
    Number parseInt parseFloat
  • object对象
    初始化 var o = new object();
    属性方法
    Constructor创建当前对象的函数
    hasOwnProperty(propertyName)给定的属性在当前实例中是否出现
    ** isPrototypeOf(object) **别传入的对象是否为另一个对象的原型
    PropertyIsEnumerable判断给定的属性是否可以用 for...in 语句进行枚举。
    toLocaleString 方法返回执行环境对应的字符串。
    toString()对象字符串表示
    valueOf()返回对象的字符串数值或布尔表示
  • 语句
    if do-while while for switch
    for-in枚举
    lable-代码中添加标签,以便后来使用???
    break 跳出整个循环 continue跳出当前循环
    with 作用域设定到一个对象中

4.变量,作用域,内存问题

执行环境类型——全局,局部
其中内部环境可通过作用域链访问所有的外部环境,但外部环境不能内部环境的任何变量和函数

4.1延长作用域链

执行下列语句时,作用域延长

try-catch
with

  • 栗子1
function buildUrl(){  
     var qs="?debug=true";  
     with(location){  
         var url=href+qs;  
     }  
     return url;  
}  
var result=buildUrl();  
alert(result);  

输出: 静态页地址+qs的值。
原因:
1.由于with语句块中作用域的‘变量对象’是只读的,所以在他本层定义的标识符,不能存储到本层,而是存储到它的上一层作用域
2.with代码块中,javascript引擎对变量的处理方式是:先查找是不是该对象的属性,如果是,则停止。如果不是继续查找是不是局部变量。

var o={href:"sssss"};  
var href="1111";  
function buildUrl(){  
     var qs="?debug=true";       
     with(o){  
          href="2222";  
          var url=href+qs;  
     }      
     return url;  
}  
var result=buildUrl();  
alert(result);  
alert(href);  

输出: 结果:2222?debug=true + 1111

4.2 JavaScript作用域之没有块级作用域

  

输出结果为:k=0 k=1 k=2 k=3 k=4 k=5 k=6 k=7 k=8 k=9 +++k=10 j=0
这是因为,由于JavaScript中不存在块级作用域,因此函数中声明的所有变量,无论是在哪里声明的,在整个函数中它们都有定义。

 

结果:第一个alert输出:underfined而不是global,第二个alert输出local
与下列函数等价

function f(){  
    var scope;  
    alert(scope);  
    var scope="local";  
    alert(scope);         
}  

5.引用类型

5.1Object类型

创建有两种方式:
1.new操作符后跟Object构造函数

var person = new Object();
person.name = "Nicholas";
person.age = 29;

2.字面量表示法

var person = {
    name: "Nicholas",
    age: 20,
    5:true
};

这里所有的数值属性名都会被转换为字符串
访问方法

alert(person.name);
alert(person[name])

5.2Arrary类型

构造
var colors = ["red","green"];
var arrays = new Array(3);//new可省略
var names = new Array["Mia"];
数组尾部添加新项
colors[colors.length] = "pink"

js判断数组类型的方法
  • instanceof
var a=[];
console.log(a instanceof Array) //返回true 
  • constructor
    constructor 属性返回对创建此对象的数组函数的引用
console.log([].constructor == Array);
console.log({}.constructor == Object);
console.log("string".constructor == String);
console.log((123).constructor == Number);
console.log(true.constructor == Boolean);

较为严谨并且通用的方法:

function isArray(object){
    return object && typeof object==='object' &&
            Array == object.constructor;
}

!!注意:

使用instaceof和construcor,被判断的array必须是在当前页面声明的!比如,一个页面(父页面)有一个框架,框架中引用了一个页面(子页面),在子页面中声明了一个array,并将其赋值给父页面的一个变量,这时判断该变量,Array == object.constructor;会返回false;
原因:
1、array属于引用型数据,在传递过程中,仅仅是引用地址的传递。
2、每个页面的Array原生对象所引用的地址是不一样的,在子页面声明的array,所对应的构造函数,是子页面的Array对象;父页面来进行判断,使用的Array并不等于子页面的Array;切记,不然很难跟踪问题!

  • 特性判断法
function isArray(object){
    return  object && typeof object==='object' &&    
            typeof object.length==='number' &&  
            typeof object.splice==='function' &&    
             //判断length属性是否是可枚举的 对于数组 将得到false  
            !(object.propertyIsEnumerable('length'));
}

有length和splice并不一定是数组,因为可以为对象添加属性,而不能枚举length属性,才是最重要的判断因子。备注:如果 proName 存在于 object 中且可以使用一个 For…In 循环穷举出来,那么 propertyIsEnumerable 属性返回 true

  • 最简单的方法
function isArray(o) {
    return Object.prototype.toString.call(o) === ‘[object Array]‘;
}

转化方法

  • arrayObject.toLocaleString()
    返回值:
    arrayObject 的本地字符串表示。
  • toString()
    把数组转换为字符串,并返回结果。返回值与没有参数的 join() 方法返回的字符串相同。
    alert(colors.join(","))
  • valueOf
    valueOf()方法返回 Array 对象的原始值。实际上,为了创建这个字符串会调用数组的每一项的toString()方法。

栈方法

栈是一种LIFO(Last-In-First-Out,后进先出)的数据结构ECMAScript为数组提供了push()和pop()方法,可以实现类似栈的行为。分别添加到数组末尾和从数组末尾移除最后一项。

队列方法

shift:从数组中把第一个元素删除,并返回这个元素的值。
unshift: 在数组的开头添加一个或更多元素,并返回新的长度
push:在数组的中末尾添加元素,并返回新的长度
pop:从数组中把最后一个元素删除,并返回这个元素的值。

unshift比push要慢差不多100倍!因此,平时还是要慎用unshift,特别是对大数组。那如果一定要达到unshift的效果,可以借助于Array的reverse方法,Array的reverse的方法能够把一个数组反转。先把要放进数组的元素用push添加,再执行一次reverse

重排序方法

reverse()——反转
sort()——升序排列数组

操作方法

  • concat()方法
    基于当前数组中所有项创建新数组。
var colors = ["red","green","blue"];
var colors2 = colors.concat("yellow",["black","brown"]);

alert(colors);
alert(colors2);

结果为colors为数组[“red”,”green”,”blue”];
colors2为数组[“red”,”green”,”blue”,”yellow”,”black”,”brown”];
concat()方法只是用当前数组重新创建一个新的数组,因此当前数组保持不变(colors数组不变

  • slice()方法
    slice()方法:基于当前数组中的一个或多个项创建一个新数组。
    slice()方法中可以有一个或者两个参数(代表数组的索引值,0,1,2……)。
    接收一个参数时:返回当前数组中从此参数位置开始到当前数组末尾间所有项。
    接收两个参数时:返回当前数组中两个参数位置间的所有项,但不返回第二个参数位置的项。
    参数也可以为负数,表示从末尾算起,-1代表最后一个,使用方法和正数一样。
var colors = ["red","green","blue","yellow","black","brown"];
var colors2 = colors.slice(2);
var colors3 = colors.slice(1,4);
var colors4 = colors.slice(2,-2);
var colors5 = colors.slice(-3,-1);

console.log(colors2);
console.log(colors3);
console.log(colors4);
console.log(colors5);

结果为:
[“blue”, “yellow”, “black”, “brown”]
[“green”, “blue”, “yellow”]
[“blue”, “yellow”]
[“yellow”, “black”]

  • splice()
    splice()主要用途是向当前数组的中间插入项,可以进行删除、插入、替换操作。会返回一个数组,包含从原始项中删除的项(若果没有删除,返回一个空数组)

删除:两个参数,删除起始项的位置和删除的项数。

var colors = ["red","green","blue"];
var removed = colors.splice(1,2);
alert(colors);      //red
alert(removed);     //green,blue

插入:在指定位置插入任意数量项,包括两个基本参数(即删除操作中的两个参数类型)和要插入项的参数,两个基本参数为起始位置和0(要删除的项数应为0项),要插入的项参数可以是任意个(”red”,”green”,”blue”)。

var colors = ["red","green","blue"];
var removed = colors.splice(1,0,"yellow","orange");
alert(colors);      //"red","yellow","orange","green","blue"
alert(removed);     //空数组

替换:向指定位置插入任意数量的项同时删除任意数量的项,插入项数和删除项数可以不同。参数包括两个基本参数(即删除操作中的两个参数类型)和要插入项的参数。

var colors = ["red","green","blue"];
var removed = colors.splice(1,1,"purple","black");
alert(colors);    //"red","purple","black","blue"
alert(removed);   //"green"                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  

位置方法

  • indexOf() 方法
    返回某个指定的字符串值在字符串中首次出现的位置。

  • lastIndexOf() 方法
    返回一个指定的字符串值最后出现的位置,在一个字符串中的指定位置从后向前搜索。

  • substr
    substr(start,length)表示从start位置开始,截取length长度的字符串。
    var src="images/off_1.png";
    alert(src.substr(7,3));
    弹出值为:off

  • substring
    substring(start,end)表示从start到end之间的字符串,包括start位置的字符但是不包括end位置的字符。
    var src="images/off_1.png";
    alert(src.substring(7,10));
    弹出值为:off

迭代方法

var arr = [3,4,5,6,7,"a"];
var isNum = function(elem,index,AAA){
return !isNaN(elem);
}
var toUpperCase = function(elem){
return String.prototype.toUpperCase.apply(elem);
}
var print = function(elem,index){
console.log(index+"."+elem);
}
  • every
    对数组中的每一项执行测试函数,直到获得对指定的函数返回 false 的项。使用此方法 可确定数组中的所有项是否满足某一条件,类似于&&的含义
var res = arr.every(isNum);
console.log(res);//false;
  • some
    对数组中的每一项执行测试函数,直到获得返回 true 的项。 使用此方法确定数组中的所有项是否满足条件.类似于||的含义
res = arr.some(isNum);
console.log(res);//true
  • filter
    对数组中的每一项执行测试函数,并构造一个新数组,返回 true的项被添加进新数组。 如果某项返回 false,则新数组中将不包含此项
res = arr.filter(isNum);
console.log(res);//[3, 4, 5, 6, 7]
  • map
    对数组中的每一项执行函数并构造一个新数组,并将原始数组中的每一项的函数结添加进新数组。
res = arr.map(toUpperCase);
console.log(res);//["3", "4", "5", "6", "7", "A"]
  • forEach
    对数组中的每一项执行函数,不返回值
res = arr.forEach(print);
console.log(res);

缩小方法

  • reduce
array.reduce(callbackfn,[initialValue])
function callbackfn(preValue,curValue,index,array){}

**preValue
**: 上一次调用回调返回的值,或者是提供的初始值(initialValue)
**curValue
**: 数组中当前被处理的数组项
**index
**: 当前数组项在数组中的索引值
**array
**: 调用 reduce()

Date类型

Date.parse接受一个字符串参数,如果可以转化,将转换为对应的毫秒数,否则返回 NaN;
Date.UTC最少接受两个参数,分别表示年份和月份(0·11),其他的日期,小时(0-24)、分钟、秒,可以指定也可以不指定,不指定时默认为 0;

dateObject.getTime() 0~... 从GTM1970年1月1日0:00:00开始计算的毫秒数。
dateObject.getYear() 70~... 指定的年份减去1900,2000年后为4位数表示的年份。
dateObject.getFullYear() 1970~... 4位数年份,适用于版本4以上的浏览器。
dateObject.getMonth() 0~11 一年中的月份(1月为0)。
dateObject.getDate() 1~31 一月中的日期。
dateObject.getDay() 0~6 星期(星期日为0)。
dateObject.getHours() 0~23 一天中指定的小时数,采用24小时制。
dateObject.getMinutes() 0~59 指定小时内的分钟数。
dateObject.getSeconds() 0~59 指定分钟内的秒数。
dateObject.setTime(val) 0~... 从GTM1970年1月1日0:00:00开始计算的毫秒数。
dateObject.setYear(val) 70~... 指定的年份减去1900,2000年后为4位数表示的年份。
dateObject.setMonth(val) 0~11 一年中的月份(1月为0)。
dateObject.setDate(val) 1~31 一月中的日期。
dateObject.setDay(val) 0~6 星期(星期日为0)。
dateObject.setHours(val) 0~23 一天中的小时数,采用24小时值。
dateObject.setMinutes(val) 0~59 指定小时内的分钟数。
dateObject.setSecond(val) 0~59 指定小时内的秒数。

当前时间

var start = Date.now()

格式化

var d = new Date();
console.log(d); // 输出:Mon Nov 04 2013 21:50:33 GMT+0800 (中国标准时间)
console.log(d.toDateString()); // 日期字符串,输出:Mon Nov 04 2013
console.log(d.toGMTString()); // 格林威治时间,输出:Mon, 04 Nov 2013 14:03:05 GMT
console.log(d.toISOString()); // 国际标准组织(ISO)格式,输出:2013-11-04T14:03:05.420Z
console.log(d.toJSON()); // 输出:2013-11-04T14:03:05.420Z
console.log(d.toLocaleDateString()); // 转换为本地日期格式,视环境而定,输出:2013年11月4日
console.log(d.toLocaleString()); // 转换为本地日期和时间格式,视环境而定,输出:2013年11月4日 下午10:03:05
console.log(d.toLocaleTimeString()); // 转换为本地时间格式,视环境而定,输出:下午10:03:05
console.log(d.toString()); // 转换为字符串,输出:Mon Nov 04 2013 22:03:05 GMT+0800 (中国标准时间)
console.log(d.toTimeString()); // 转换为时间字符串,输出:22:03:05 GMT+0800 (中国标准时间)
console.log(d.toUTCString()); // 转换为世界时间,输出:Mon, 04 Nov 2013 14:03:05 GMT

RegExp

RegExp详谈

function

函数内部属性只要包括两个特殊的对象:arguments和this。
函数属性包括:length和prototype
函数方法(非继承)包括:apply()和call()
继承而来的函数方法:bind()、toString()、toLocaleString()、valueOf()

  • 没有重载
    js不存在重载的概念,后面的方法会覆盖先前的同名的方法。
  • 函数声明和函数表达式
    1)函数声明(Function Declaration);
    // 函数声明
    function funDeclaration(type){
        return type==="Declaration";
    }

2)函数表达式(Function Expression)。

    // 函数表达式
    var funExpression = function(type){
        return type==="Expression";
    }

上面的代码看起来很类似,感觉也没什么太大差别。但实际上,Javascript函数上的一个“陷阱”就体现在Javascript两种类型的函数定义上。下面看两段代码:

     funDeclaration("Declaration");//=> true
     function funDeclaration(type){
         return type==="Declaration";
     }
     funExpression("Expression");//=>error
     var funExpression = function(type){
         return type==="Expression";
     }

用函数声明创建的函数funDeclaration可以在funDeclaration定义之前就进行调用;而用函数表达式创建的funExpression函数不能在funExpression被赋值之前进行调用。
代码1段JS函数等同于:

    function funDeclaration(type){
        return type==="Declaration";
    }
    funDeclaration("Declaration");//=> true

代码2段JS函数等同于:

    var funExpression;
    funExpression("Expression");//==>error
    funExpression = function(type){
        return type==="Expression";
    }

再来个例子

var sayHello;
    console.log(typeof (sayHey));//=>function    
    console.log(typeof (sayHo));//=>undefined
    if (true) {
        function sayHey() {
            console.log("sayHey");
        }
        sayHello = function sayHo() {
            console.log("sayHello");
    }
    } else {
        function sayHey() {
            console.log("sayHey2");
        }
        sayHello = function sayHo() {
            console.log("sayHello2");
        }
    }    
    sayHey();// => sayHey2    
    sayHello();// => sayHello

等同于

var sayHello;
    function sayHey() {
            console.log("sayHey");
        }
    function sayHey() {
            console.log("sayHey2");
    }
    console.log(typeof (sayHey));//=>function    
    console.log(typeof (sayHo));//=>undefined
    if (true) {
        //hoisting...
        sayHello = function sayHo() {
            console.log("sayHello");
    }
    } else {
        //hoisting...
        sayHello = function sayHo() {
            console.log("sayHello2");
        }
    }    
    sayHey();// => sayHey2    
    sayHello();// => sayHello

Javascript 中函数声明和函数表达式是存在区别的,函数声明在JS解析时进行函数提升,因此在同一个作用域内,不管函数声明在哪里定义,该函数都可以进行调用。而函数表达式的值是在JS运行时确定,并且在表达式赋值完成后,该函数才能调用。这个微小的区别,可能会导致JS代码出现意想不到的bug,让你陷入莫名的陷阱中。

  • 函数的内部属性
  • arguments
    有两个特殊的对象:arguments和this。其中,arguments它是一个类数组对象,包含着传入函数中的所有参数。虽然arguments的主要用途是保存函数参数,但这个对象含所有一个名叫callee的属性,该属性是一个指针,指向拥有这个arguments对象的函数。
    下面这个非常经典的阶乘函数。
function factorial (num){
  if(num <= 1){
    return 1;
  } else{
  return num * factorial(num-1); 
  }
}

定义阶乘函数一般都会用到递归算法,如上面代码所示,在有函数名字,并且函数名字以后也不会改变的情况下,这种定义没问题。但是这个函数的执行与函数名factorial紧紧耦合在了一起,为了消除这种紧密耦合现象(函数名字改变等情况),可以使用arguments.callee。

function factorial(num){
if(num<=1){
  return 1;
  } else{
    return num * arguments.callee(num-1);
  }
}

在这个重写后的factorial()函数的函数体内,没有再引用函数名factorial。这样,无论引用函数时使用的是什么名字,都可以保证正常完成递归调用。例如:

var trueFactorial=factorial;
factorial=function()
{
    return 0;
};
 
alert(trueFactorial(5));//120
alert(factorial(5));//0

在此,变量trueFactorial获得了factorial的值,实际上是另一个位置上保存了一个函数的指针。然后,我们又将一个简单的返回了0的函数赋值给了factorial变量。如果像原来factorial()那样不用使用arguments.callee,调用trueFactorial()就会返回0。可是,在解除了函数体内的代码与函数名的耦合状态之后,trueFactorial()仍然能够正常的计算阶乘;至于factorial(),它现在只是一个返回0的函数。

  • this
     函数内部的另一个特殊对象是this,其行为与java和c#中的this大致类似。换句话说,this引用的是函数据以执行的环境对象,或则也可以说是this值(当在网页的全局作用中调用函数时,this对象引用的就是window)。如下例子:
window.color="red";
var o={color:"blue"};
 
function sayColor()
{
    alert(this.color);
}
sayColor();//red
 
o.sayColor=sayColor;
o.sayColor();//blue
  • apply()方法
    接收两个参数:一个是在其中运行函数的作用域,另一个是参数数组。其中,第二个参数可以使Array的实例,也可以是arguments对象
unction sum(num1,num2)
{
    return num1+num2;
}
 
function callSum1(num1,num2)
{
    return sum.apply(this,arguments);//传入arguments对象
}
 
function callSum2(num1,num2)
{
    return sum.apply(this,[num1,num2]);//传入数组
}
 
alert(callSum1(10,10));//20
alert(callSum2(10,10));//20
  • call()方法
    对于call()方法而言,第一个参数是this值没有变化,变化的是其余参数都直接传递给函数。换句话讲,在使用call()方法时,传递给函数的参数必须逐个列举出来,如下例子:
function sum(num1,num2)
{
    return num1+num2;
}
 
function callSum(num1,num2)
{
    return sum.call(this,num1,num2);
}
 
alert(callSum(10,10));//20

事实上,传递参数并非apply()和call()正真的用武之地;它们正真强大的地方是能够扩充函数赖以运行的作用域。如下例子:

window.color="red";
var o={color:"blue"};
 
function sayColor()
{
    alert(this.color);
}
 
sayColor();//red
 
sayColor.call(this);//red
sayColor.call(window);//red
sayColor.call(o);//blue

这一次,sayColor()也是作为全局函数定义的,而且当在全局作用域中调用它时,它确实会显示“red”,因为对this.color的求值会转换成对window.color的求值。而sayColor.call(this)和sayColor.call(window),则是两种显示的在全局作用域中调用函数的方式,结果当然都会显示“red”。但是,当运行sayColor.call(o)时,函数的执行环境就不一样了,因为此时函数体内的this对象指向了o,于是结果显示的是“blue”。
使用call()或apply()来扩充作用域的最大好处,就是对象不需要与方法有任何耦合关系。在前面例子的第一个版本中,我们是先将sayColor()函数放到了对象o中,然后再通过o来调用它;而在这里重写的例子中,就不需要先前那个多余的步骤了。

  • bind()
window.color="red";
var o={color:"blue"};
 
function sayColor()
{
    alert(this.color);
}
 
var objectSayColor=sayColor.bind(o);
objectSayColor();//blue

在这里,sayColor()调用bind()并传入对象o,创建了objectSayColor()函数。objectSayColor()函数的this值等于o,因此即使是在全局作用域中调用这个函数,也会看到“blue”。

函数表达式

理解作用域链是理解闭包的关键;

(1)什么是执行环境

执行环境有二种: 全局执行环境;局部执行环境---function里面;

执行流进入函数执行时,创建执行环境;函数执行结束,回收!

(2)变量对象

理解变量对象非常重要,变量对象在函数中也称为 活动对象,我还是喜欢说成变量对象。

每一个执行环境都有一个变量对象,变量对象会保存这个执行环境的变量和方法;我们不难想到全局的变量对象是谁? window对象

我们在全局中添加变量和方法都是添加到window里。函数执行时 执行环境中就会创建变量对象;一般来说:函数调用完毕之后无论是执行环境(出栈),还是变量对象都是回收的。闭包的出现使得变量对象不回收;

(3)作用域链?

什么是作用域链:作用域链的本质是 指向变量对象的一组有序指针;字面上很难理解,我第一次看不明白!

有什么作用:在一个执行环境中对变量和方法进行有序(正确)访问;

什么时候创建: 一个函数声明的时候就创建了,作用域链会保存在一个函数的内部属性[[Scope]]中;

注意:执行流进入函数是: 创建执行环境->将作用域链(利用[[Scope]]属性) 复制到执行环境中->创建变量对象(活动对象)->将变量对象的引用(指针)导入作用域链的最前端->执行代码

具体请看下面的代码

  function compare(value1,value2){
         if(value1value2){return -1;}
          else{return 0;}
       }
      var result=compare(5,10)//1

我们可以看到,作用域链是什么:有序指针,前面说的作用域最前端在就是0位置。 查找变量或者函数是最前端开始的,0指向的活动对象没有你要找的变量,再去1找。0-1其实

从代码的角度上看其实就是从函数内部一直向函数外部找标识符(变量或者函数),找到立即停止,不会再向上查找。这就是作用域链!

  • 闭包
    定义: 闭包是有权访问另外一个函数作用域变量的函数;注意闭包是一个函数!
    创建闭包的主要的方法:在一个函数里面创建一个函数(闭包); 比如

在这里匿名函数是一个闭包,一般情况下: 一个函数执行完毕之后,无论是作用域链还是变量对象都会回收,但是这个匿名函数的作用域链里有A()变量对象的引用,所以没有消除。

还可以访问变量对象的num;这就是闭包的好处!但是闭包的出现加大内存负担,就这里而已,我们即使后面不再使用B函数了,A()变量对象也不会消失,javascript不知道你什么时候还会再用,当然我们可以这样 B=null; 这样的话匿名函数没有引用,被回收,A()变量对象也一起回收!

《javascript高级程序设计》中尼古拉斯大神建议我们:可以不使用闭包尽量不使用,万不得已才使用!

  • 块级作用域

    我们都知道javascript是没有块级作用域的,比如{}; if(i=0){} while(){} for(){}这些都不会形成块级作用域; 那么怎么创建 java c#类似功能的块级作用域?

    语法:

(function(){

//块级作用域

})();

注意: 我们创建一个匿名函数,立即调用,里面的代码都要运行一遍,而在window中看不见,这不就是块级作用域吗? 还有一个好处,这个匿名函数没有指针,

调用后回收,不会产生垃圾(里面的方法,变量都不需要再访问的)。简直就是完美的块级作用域! 注意格式 (匿名函数)其实就是一个指针,再加上()就是调用了。

  • 构造函数中的闭包

    (1) 我们知道怎么为对象添加'私有变量' 这样

function Person(name,age){
    this.name=name;//共有属性
    this.age=age;
    
    var school="一中"; //私有属性
    this.GetSchool=function (){return school;}
}

我们这个school是私有的变量,因为闭包的原因, 对象实例自然可以访问这个变量; 比如 var p=new Person('nos',20); p.GetSchool(); // 一中;

 (2)现在来一个奇葩: 静态私有属性 ;
(function(){

         var school=""; //静态私有;

        Person=function(name,age,value){ //构造函数

         this.name=name;this.age=age;

          school=value;

     }; 

    Person.prototype.GetSchool=function(){alert(school);}

         })();
      var p=new Person('andy',21,'一中');
      p.GetSchool();//一中
      var pp=new Person('nos',29,'二中');
      pp.GetSchool();//二中
      p.GetSchool();//二中

从结果上看 school是对象共有的,私有的属性, 即静态私有属性;
我们看看构造函数是怎么定义的: 没有使用var ,前面说过即使在函数里面定义,没有使用var申明,就是window的,为全局的。自然可以在全局使用!

  这个匿名函数中调用了一次,只产生一个对象变量,school都是同一个,实现共享。

你可能感兴趣的:(js高级程序设计读书笔记)