JS基础——作用域、原型、闭包初探(上——变量及作用域)

(本文来自红宝书第三版1-7章的读后梳理)

前七章是JS高级程序设计的经典,这七章的内容主要讲解了JavaScript的基础知识,涉及到基础概念作用域及执行环境引用类型面向对象的程序设计(这里主要讲到关于对象的创建和继承——原型模型以及原型链、寄生组合式继承等等)、函数表达式(函数提升、闭包、模仿块级作用域、模块模式等等)。

总共有七章,前两章主要讲的是JavaScript的前世今生以及如何在HTML中使用JavaScript脚本、

重点在3-7章,依次是:“基础概念”,“变量、作用域和内存问题”,“引用类型”、“面向对象的程序设计”、“函数表达式”。

下面将依次介绍:


一、JavaScript简介

记住这个:


二、在HTML中使用JavaScript


三、基本概念

从目录开始梳理:语法、关键字和保留字、变量、数据类型、操作符、语句、函数。

1、语法(区分大小写、标识符、注释、严格模式、语句)

区分大小写:test与Test变量名表示两个不同的变量

标识符:函数名、变量名、参数名、属性名都是标识符。

第一个字符必须是一个字母、下划线(_)或者美元符号($)

标识符采用驼峰大小写格式(第一个字符小写,剩下的每一个单词的首字母大写)

注释

严格模式:“use strict”开启严格模式。

function doSomething(){
    "use strict";
    //函数体
}

语句: 

2、关键字和保留字

3、变量

  1. var message = 12;
    message = "string";//有效,但不推荐
  2. 在函数中省略var操作符可以定义全部变量,但不推荐。

    function test(){
        message = "hi";//全局变量
    }
  3. 一条语句导定义多个变量,用逗号分开。

    var message = "hi",
        found = false,
        age = 29;

4、数据类型(typeof操作符、Undefined、Null、Boolean、Number、String、object)

  1. typeof操作符:“undefined”、“boolean”、“number”、“string”、“object”、“function”。
  2. Undefined类型:未初始化(var message;)的变量,没有声明的变量。
  3. Null类型:表示空对象指针,typeof返回为“object”,undefined派生自null。
    alert(null == undefined);    //true
    alert(null === undefined);    //false
  4. Boolean类型

    数据类型true值false值
    Booleantruefalse
    Number非零数值0和NaN
    String非空字符串“”(空字符串)
    Undefinedn/a或N/Aundefined
    Object任何对象null
  5.  Number类型

    1.       0.1+0.2 !=0.3(基于IEEE754数值的浮点计算通病,具体原因不在此详细介绍,看这里);

    2. Number.MIN_VALUE、Number.Max_VALUE;

    3. Number.NEGATIVE_INFINITY、Number.POSITIVE_INFINITY;

    4. NaN、isNaN();

    5. Number()、parseInt()、parseFloat();

      //ES5不具有解析8进制的能力,所以
      var num = parseInt("070");    //在ES5的引擎中为70,ES3为56
      
      //为parseInt()函数指定基数
      var num2 = parseInt("AF",16);    //175
      var num3 = parseInt("AF");      //NaN

      Number()可以应用于任何数据类型,而parseInt()、parseFloat()专门用于将字符串转换为数值。

  6. String类型

    1. 字符字面量:\n、\t、\b、\r、\f、    \\、\'、\''、\xnn、\unnnn;

    2. toString()、String():

      1. toString()不能转换Undefined、Null(因为Undefined、Null值没有这个方法);

      2. toString()可以传递基数——“2”、“8”、“10”、“16”表示输出 相应的进制数值 表示的 字符串。

      3. String()可以在遇到其他数据类型时调用toString(),在遇到Undefined、Null时输出字符串“undefined”、“null”。

  7. Object类型:ECMAScript中Object是所有对象的基础。因此所有对象都具有如下方法。(注意BOM和DOM中的对象是宿主对象,由宿主实现提供和定义。ECMAScript不负责定义宿主对象,因此宿主对象可能不继承Object)

    1. constructor:构造函数,保存着用于创建当前对象的函数;

    2. hasOwnProperty(propertyName):给定属性是在当前实例中,而不是原型中(后面介绍原型的概念)

    3. isPrototypeOf(object):传入的对象是否当前对象的原型;

    4. propertyIsEnumerable(properName):给定的属性是否能够用for-in来枚举;

    5. toString():返回对象的字符串表示;

    6. toLocaleString():返回对象的字符串表示,与执行环境对应;

    7. valueOf():返回对象的字符串、数值、布尔值表示。通常于toString()返回的值相同。

5、操作符(一元、位、布尔乘性、加性关系、相等、条件赋值、逗号

  1.  一元操作符:只能操作一个值的操作符。
    1. ++、--;
    2. ++、--   前置与后置的副效应;
    3. +、-  放在非数值数据类型前面时,执行Number()转型函数,转换为数值。
  2. 位操作符:ECMAScript中的所有数值都以IEEE754 64位格式储存,但位操作符是先将64位的值转换成32位的整数,然后执行操作,最后再将结果转换回64位。
    1. 对于有符号的整数,32位的前31位表示整数的值,第32位是符号位:0为正数,1为负数。
    2. 负数绝对值的二进制码 ——>反码——>加1——>补码(该负数的二进制码);
    3. ~(按位非,负值-1)、&(按位与)、|(按位或)、^(按位异或);
    4. <<(左移,所有数值向左移动,右边空出来的位置添0,x*2^{n},x为移动前的值,n为移动位数);
    5. >>(有符号右移,即保留符号位,且用符号位的值填充左边出现的空位,x/2^{n});
    6. >>>(无符号右移,不保留符号位,用0填充符号位)。
  3. 布尔操作符:&&、||、!。
    1. &&、|| 都是短路操作符,即如果第一个操作能够决定结果,就不会再对第二操作数求值;
  4. 乘性操作符:*、/、%。
    1. *(乘):0*Infinty = NaN;
    2. /(除):(以下Infinity都简写为In)
      1. In / In = NaN;
      2. 非零的有限数,被零除,值为\pmInfinity,正负取决于有符号操作数的符号;
      3. Infinity,被任何非零的数值除,结果为\pmInfinity,正负取决于有符号操作数的符号;
    3. %(求模):
      1. In % In = NaN;
      2. In % Num = NaN;
      3. Num % 0 = NaN;
  5. 加性操作符:+、-。
    1. +(加):如果一个值是非数值,String、Object或其他,则将值转换为字符串,字符串拼接操作
    2. -(减):如果值是非数值,则将其转换为数值,然后执行减法操作
  6. 关系操作符:>、<、>=、<=。
    var result = "23"<"3";    //true,两个都为字符串,比较的是字符串的编码值,“2”的字符编码50,“3”为51
    var result = "23"<3;     //false,字符串“23”转换为数值后进行比较
    var result = "a"<3;     //false,因为a转换成了NaN
  7. 相等操作符:==,!=,===,!==。

    1. ==、!=:会进行类型转换(强制转型);

    2. ===、!==(全等与不全等):严格的比较,不进行强制转换,能够保持代码中数据类型的完整性,推荐使用

      var result = (undefined == null);    //true
      var result = (undefined === null);   //false
  8. 条件操作符:statement ?trueValue:falseValue。

  9. 赋值操作符:=、+=、-=、*=、/=、%=、<<=、>>=、>>>=

  10. 逗号操作符:逗号操作符总是会返回表达式中的最后一项。

    var num = (12,23,34,45,65,0);    //0

6、语句(if、do-while、whilefor、for-in、labelbreak&continue、switch、with

ECMAScript中的流控制语句,这不详细介绍了,懂的都懂    :)。

label语句可以在代码中添加标签,然后与break和continue语句联用,从而返回到代码中的特定位置。

建议如果使用label语句,一定要使用描述性的标签,同时不要嵌套过多的循环。使用过度会给调试带来很多麻烦。

with是将代码的作用设置到一个特定的对象,但是大量使用with语句会导致性能下降,在开发大型应用程序时,不建议使用with。

严格模式下,不允许使用with语句。

7、函数(理解参数、没有重载)

  1. 理解参数:function(num1,num2)
    1. 定义的函数只接受两个参数,在调用时未必一定要传递两个参数,可以传递1或多个参数;
    2. ECMAScript中的参数用一个数组来表示,用arguments对象来访问,arguments对象与数组类似但不是Array实例,arguments[0]对应num1,arguments[1]对应num2
    3. arguments.length;传递给函数的参数个数。
    4. arguments.[0] = 12;修改num1的值。
    5. arguments[0]+num2,就是num1+num2;
    6. arguments的值永远与对应命名参数的值保持同步;不过他们的内存空间是独立的,只是值会同步
    7. 如果只传递了一个参数,num2为undefined,arguments[1]设置的值不会反映到命名参数中。因为arguments对象的长度是由传入的参数个数决定,而不是定义的参数个数决定。
    8. 严格模式下,重写arguments的值都是无效的
  2. 没有重载
    1. 因为函数参数是有数组来表示的,而没有函数签名,真正的重载是不可能做到的。
    2. ECMAScript中的同名函数,该名字只属于后定义的函数。

四、变量、作用域和内存问题

1、基本类型和引用类型的值(动态的属性、复制变量值、传递参数、检测类型)

Undefined、Null、Boolean、Number、String都是基本类型的值(简单的数据段),Object是引用类型的值(可能是由多个值构成的对象)。

  1. 动态的属性:可以为变量名设置相应的属性值,来增加或者修改属性值。但对于基本类型的值进行这个操作是没有意义的(即便设置了,也都会只是undefined)。
  2. 复制变量值
    1. 基本类型值的复制,就是值的复制:
    2. 引用类型值的复制,复制引用类型的值,实际上是一个指针,指向堆内存中的值:
  3. 传递参数:传递参数也是同复制变量值的方式传递的,即值传递。
  4. 检测类型
    1. instanceOf()检测某个引用类型是否为另一个对象的实例。
    2. typeof检测基本类型数据,更好。

2、执行环境及作用域(没有块级作用域、延长作用域链)

这里的内容相当的重要,是后面闭包等知识点的基石。

执行环境:execution context

  1. 执行环境定义了变量或函数有权访问的其他数据,决定了它们各自的行为;
  2. 每个函数都有一个自己执行的环境;当执行流进入函数时,函数的环境会被推入到一个环境栈,当函数执行完毕后,环境栈会将函数的环境弹出,把控制权返回之前的执行环境;
  3. 最外层的执行环境是全局环境。在Web中,就是window对象,只有在卸载Web程序(关闭浏览器时),全局执行环境才会被销毁。
  4. 执行环境都有一个与之关联的变量对象(variable object),变量对象中保存着执行环境中定义的的变量及函数。

作用域:scope chain

  1. 当代码在环境中执行时,会创建变量对象的一个作用域链(scope chain)
  2. 作用:保证对执行环境有权访问的变量和函数的有序访问。
  3. 作用域链的前端是当前执行环境的变量对象,下一个是外包环境的变量对象,最外层是全局变量对象;
  4. 如果环境是一个函数,函数会将活动对象(activation object)作为变量对象。活动对象一开始只包含arguments对象。
  5. 标志符的解析是沿着作用域链,一级一级向上搜索的过程。(内部环境中的变量可以访问外部环境中的变量,而反过来不行)

没有块级作用域:

  • 在for循环中定义的循环计数变量i,在for完成后,不会被销毁,而存在于环境当中;
  • 在for循环中定义的变量,在for循环完成后,不会被销毁,仍在存在于当前环境当中。

延长作用域链:

  • 当函数中遇到try-catch块时,catch块会在作用链中添加一个变量对象;
  • 当函数中遇到with时,会在作用域中添加一个变量对象。

3、垃圾收集(标记清除、引用计数、性能问题、管理内存)

  1. 标记清除:
    1. 垃圾收集器会扫描内存中的所有变量,并打上标记;
    2. 然后将正在运行环境中的变量,以及被该环境中变量引用的变量的标记去掉;
    3. 完成扫描后,还带有标记的变量会被清除掉。
  2. 引用计数:
    1. 一个数据被赋值给一个变量,就被引用了1次,赋值给另一个变量,应用次数就+1;
    2. 当该变量引用了其他数据,即不再引用这个数据时,这个数据的引用次数-1;
    3. 当数据的引用次数为0时,垃圾处理器就会将该数据从内存中清除。
    4. 引用计数有一个很大的问题就是循环引用(即变量的引用次数永远不会为0),例:
      var objectA = new object();
      var objectB = new object();
      
      objectA.someObject = objectB;
      objectB.someObject = objectA;
  3. 性能问题

    确定垃圾收集器的时间间隔是一个非常重要的问题。
  4. 管理内存:

    1. 系统分配给浏览器的内存比桌面应用更加的少,所以Web的运行内存管理更加的苛刻。

    2. 应该及时将内存中不用的变量销毁——解除引用。(给变量打上标记,当下一次垃圾收集器运行的时候,就会将响应的数据清除)

      function createPerson(name){
          var localPerson = new Person();
          loaclPerson.name = name;
          return localPerson;
      }
      
      var globalPerson = createPerson("Nicholas");
      
      //手工解除globalPerson的引用
      globalPerson = null;

附:进一步的探索

  1. JavaScript对象的底层数据结构是什么
  2. Symbol类型在实际开发中应用,可手动实现一个简单的Symbol
  3. JavaScript中的变量在内存中的具体存储形式
  4. 基本类型对应的内置对象,以及他们之间的装箱拆箱操作
  5. 理解值类型和引用类型
  6. nullundefined的区别
  7. 至少说出三种判断JavaScript数据类型的方式,以及它们的优缺点,如何准确判断数组类型
  8. 可能发生隐式类型转换的场景以及转换原则,应如何避免或巧妙应用
  9. 出现小数精度丢失的原因,JavaScript可以存储的最大数字、最大安全数字,JavaScript处理大数字的方法,避免精度丢失的方法
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值