(本文来自红宝书第三版1-7章的读后梳理)
前七章是JS高级程序设计的经典,这七章的内容主要讲解了JavaScript的基础知识,涉及到基础概念、作用域及执行环境、引用类型、面向对象的程序设计(这里主要讲到关于对象的创建和继承——原型模型以及原型链、寄生组合式继承等等)、函数表达式(函数提升、闭包、模仿块级作用域、模块模式等等)。
总共有七章,前两章主要讲的是JavaScript的前世今生以及如何在HTML中使用JavaScript脚本、
重点在3-7章,依次是:“基础概念”,“变量、作用域和内存问题”,“引用类型”、“面向对象的程序设计”、“函数表达式”。
下面将依次介绍:
一、JavaScript简介
记住这个:
二、在HTML中使用JavaScript
三、基本概念
从目录开始梳理:语法、关键字和保留字、变量、数据类型、操作符、语句、函数。
1、语法(区分大小写、标识符、注释、严格模式、语句)
区分大小写:test与Test变量名表示两个不同的变量
标识符:函数名、变量名、参数名、属性名都是标识符。
第一个字符必须是一个字母、下划线(_)或者美元符号($)
标识符采用驼峰大小写格式(第一个字符小写,剩下的每一个单词的首字母大写)
注释:
严格模式:“use strict”开启严格模式。
function doSomething(){
"use strict";
//函数体
}
语句:
2、关键字和保留字
3、变量
-
var message = 12; message = "string";//有效,但不推荐
-
在函数中省略var操作符可以定义全部变量,但不推荐。
function test(){ message = "hi";//全局变量 }
-
一条语句导定义多个变量,用逗号分开。
var message = "hi", found = false, age = 29;
4、数据类型(typeof操作符、Undefined、Null、Boolean、Number、String、object)
- typeof操作符:“undefined”、“boolean”、“number”、“string”、“object”、“function”。
- Undefined类型:未初始化(var message;)的变量,没有声明的变量。
- Null类型:表示空对象指针,typeof返回为“object”,undefined派生自null。
alert(null == undefined); //true alert(null === undefined); //false
-
Boolean类型:
数据类型 true值 false值 Boolean true false Number 非零数值 0和NaN String 非空字符串 “”(空字符串) Undefined n/a或N/A undefined Object 任何对象 null -
Number类型:
-
0.1+0.2 !=0.3(基于IEEE754数值的浮点计算通病,具体原因不在此详细介绍,看这里);
-
Number.MIN_VALUE、Number.Max_VALUE;
-
Number.NEGATIVE_INFINITY、Number.POSITIVE_INFINITY;
-
NaN、isNaN();
-
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()专门用于将字符串转换为数值。
-
-
String类型:
-
字符字面量:\n、\t、\b、\r、\f、 \\、\'、\''、\xnn、\unnnn;
-
toString()、String():
-
toString()不能转换Undefined、Null(因为Undefined、Null值没有这个方法);
-
toString()可以传递基数——“2”、“8”、“10”、“16”表示输出 相应的进制数值 表示的 字符串。
-
String()可以在遇到其他数据类型时调用toString(),在遇到Undefined、Null时输出字符串“undefined”、“null”。
-
-
-
Object类型:ECMAScript中Object是所有对象的基础。因此所有对象都具有如下方法。(注意BOM和DOM中的对象是宿主对象,由宿主实现提供和定义。ECMAScript不负责定义宿主对象,因此宿主对象可能不继承Object)
-
constructor:构造函数,保存着用于创建当前对象的函数;
-
hasOwnProperty(propertyName):给定属性是在当前实例中,而不是原型中(后面介绍原型的概念)
-
isPrototypeOf(object):传入的对象是否当前对象的原型;
-
propertyIsEnumerable(properName):给定的属性是否能够用for-in来枚举;
-
toString():返回对象的字符串表示;
-
toLocaleString():返回对象的字符串表示,与执行环境对应;
-
valueOf():返回对象的字符串、数值、布尔值表示。通常于toString()返回的值相同。
-
5、操作符(一元、位、布尔、乘性、加性、关系、相等、条件、赋值、逗号)
- 一元操作符:只能操作一个值的操作符。
- ++、--;
- ++、-- 前置与后置的副效应;
- +、- 放在非数值数据类型前面时,执行Number()转型函数,转换为数值。
- 位操作符:ECMAScript中的所有数值都以IEEE754 64位格式储存,但位操作符是先将64位的值转换成32位的整数,然后执行操作,最后再将结果转换回64位。
- 对于有符号的整数,32位的前31位表示整数的值,第32位是符号位:0为正数,1为负数。
- 负数绝对值的二进制码 ——>反码——>加1——>补码(该负数的二进制码);
- ~(按位非,负值-1)、&(按位与)、|(按位或)、^(按位异或);
- <<(左移,所有数值向左移动,右边空出来的位置添0,,x为移动前的值,n为移动位数);
- >>(有符号右移,即保留符号位,且用符号位的值填充左边出现的空位,);
- >>>(无符号右移,不保留符号位,用0填充符号位)。
- 布尔操作符:&&、||、!。
- &&、|| 都是短路操作符,即如果第一个操作能够决定结果,就不会再对第二操作数求值;
- 乘性操作符:*、/、%。
- *(乘):0*Infinty = NaN;
- /(除):(以下Infinity都简写为In)
- In / In = NaN;
- 非零的有限数,被零除,值为Infinity,正负取决于有符号操作数的符号;
- Infinity,被任何非零的数值除,结果为Infinity,正负取决于有符号操作数的符号;
- %(求模):
- In % In = NaN;
- In % Num = NaN;
- Num % 0 = NaN;
- 加性操作符:+、-。
- +(加):如果一个值是非数值,String、Object或其他,则将值转换为字符串,字符串拼接操作。
- -(减):如果值是非数值,则将其转换为数值,然后执行减法操作。
- 关系操作符:>、<、>=、<=。
var result = "23"<"3"; //true,两个都为字符串,比较的是字符串的编码值,“2”的字符编码50,“3”为51 var result = "23"<3; //false,字符串“23”转换为数值后进行比较 var result = "a"<3; //false,因为a转换成了NaN
-
相等操作符:==,!=,===,!==。
-
==、!=:会进行类型转换(强制转型);
-
===、!==(全等与不全等):严格的比较,不进行强制转换,能够保持代码中数据类型的完整性,推荐使用。
var result = (undefined == null); //true var result = (undefined === null); //false
-
-
条件操作符:statement ?trueValue:falseValue。
-
赋值操作符:=、+=、-=、*=、/=、%=、<<=、>>=、>>>=
-
逗号操作符:逗号操作符总是会返回表达式中的最后一项。
var num = (12,23,34,45,65,0); //0
6、语句(if、do-while、while、for、for-in、label、break&continue、switch、with)
ECMAScript中的流控制语句,这不详细介绍了,懂的都懂 :)。
label语句可以在代码中添加标签,然后与break和continue语句联用,从而返回到代码中的特定位置。
建议如果使用label语句,一定要使用描述性的标签,同时不要嵌套过多的循环。使用过度会给调试带来很多麻烦。
with是将代码的作用设置到一个特定的对象,但是大量使用with语句会导致性能下降,在开发大型应用程序时,不建议使用with。
严格模式下,不允许使用with语句。
7、函数(理解参数、没有重载)
- 理解参数:function(num1,num2)
- 定义的函数只接受两个参数,在调用时未必一定要传递两个参数,可以传递1或多个参数;
- ECMAScript中的参数用一个数组来表示,用arguments对象来访问,arguments对象与数组类似但不是Array实例,arguments[0]对应num1,arguments[1]对应num2;
- arguments.length;传递给函数的参数个数。
- arguments.[0] = 12;修改num1的值。
- arguments[0]+num2,就是num1+num2;
- arguments的值永远与对应命名参数的值保持同步;不过他们的内存空间是独立的,只是值会同步。
- 如果只传递了一个参数,num2为undefined,arguments[1]设置的值不会反映到命名参数中。因为arguments对象的长度是由传入的参数个数决定,而不是定义的参数个数决定。
- 严格模式下,重写arguments的值都是无效的。
- 没有重载:
- 因为函数参数是有数组来表示的,而没有函数签名,真正的重载是不可能做到的。
- ECMAScript中的同名函数,该名字只属于后定义的函数。
四、变量、作用域和内存问题
1、基本类型和引用类型的值(动态的属性、复制变量值、传递参数、检测类型)
Undefined、Null、Boolean、Number、String都是基本类型的值(简单的数据段),Object是引用类型的值(可能是由多个值构成的对象)。
- 动态的属性:可以为变量名设置相应的属性值,来增加或者修改属性值。但对于基本类型的值进行这个操作是没有意义的(即便设置了,也都会只是undefined)。
- 复制变量值:
- 基本类型值的复制,就是值的复制:
- 引用类型值的复制,复制引用类型的值,实际上是一个指针,指向堆内存中的值:
- 传递参数:传递参数也是同复制变量值的方式传递的,即值传递。
- 检测类型:
- instanceOf(),检测某个引用类型是否为另一个对象的实例。
- typeof检测基本类型数据,更好。
2、执行环境及作用域(没有块级作用域、延长作用域链)
这里的内容相当的重要,是后面闭包等知识点的基石。
执行环境:execution context
- 执行环境定义了变量或函数有权访问的其他数据,决定了它们各自的行为;
- 每个函数都有一个自己执行的环境;当执行流进入函数时,函数的环境会被推入到一个环境栈,当函数执行完毕后,环境栈会将函数的环境弹出,把控制权返回之前的执行环境;
- 最外层的执行环境是全局环境。在Web中,就是window对象,只有在卸载Web程序(关闭浏览器时),全局执行环境才会被销毁。
- 执行环境都有一个与之关联的变量对象(variable object),变量对象中保存着执行环境中定义的的变量及函数。
作用域:scope chain
- 当代码在环境中执行时,会创建变量对象的一个作用域链(scope chain)。
- 作用:保证对执行环境有权访问的变量和函数的有序访问。
- 作用域链的前端是当前执行环境的变量对象,下一个是外包环境的变量对象,最外层是全局变量对象;
- 如果环境是一个函数,函数会将活动对象(activation object)作为变量对象。活动对象一开始只包含arguments对象。
- 标志符的解析是沿着作用域链,一级一级向上搜索的过程。(内部环境中的变量可以访问外部环境中的变量,而反过来不行)
没有块级作用域:
- 在for循环中定义的循环计数变量i,在for完成后,不会被销毁,而存在于环境当中;
- 在for循环中定义的变量,在for循环完成后,不会被销毁,仍在存在于当前环境当中。
延长作用域链:
- 当函数中遇到try-catch块时,catch块会在作用链中添加一个变量对象;
- 当函数中遇到with时,会在作用域中添加一个变量对象。
3、垃圾收集(标记清除、引用计数、性能问题、管理内存)
- 标记清除:
- 垃圾收集器会扫描内存中的所有变量,并打上标记;
- 然后将正在运行环境中的变量,以及被该环境中变量引用的变量的标记去掉;
- 完成扫描后,还带有标记的变量会被清除掉。
- 引用计数:
- 一个数据被赋值给一个变量,就被引用了1次,赋值给另一个变量,应用次数就+1;
- 当该变量引用了其他数据,即不再引用这个数据时,这个数据的引用次数-1;
- 当数据的引用次数为0时,垃圾处理器就会将该数据从内存中清除。
- 引用计数有一个很大的问题就是循环引用(即变量的引用次数永远不会为0),例:
var objectA = new object(); var objectB = new object(); objectA.someObject = objectB; objectB.someObject = objectA;
-
性能问题
确定垃圾收集器的时间间隔是一个非常重要的问题。 -
管理内存:
-
系统分配给浏览器的内存比桌面应用更加的少,所以Web的运行内存管理更加的苛刻。
-
应该及时将内存中不用的变量销毁——解除引用。(给变量打上标记,当下一次垃圾收集器运行的时候,就会将响应的数据清除)
function createPerson(name){ var localPerson = new Person(); loaclPerson.name = name; return localPerson; } var globalPerson = createPerson("Nicholas"); //手工解除globalPerson的引用 globalPerson = null;
-
附:进一步的探索
- JavaScript对象的底层数据结构是什么
- Symbol类型在实际开发中应用,可手动实现一个简单的Symbol
- JavaScript中的变量在内存中的具体存储形式
- 基本类型对应的内置对象,以及他们之间的装箱拆箱操作
- 理解值类型和引用类型
- null和undefined的区别
- 至少说出三种判断JavaScript数据类型的方式,以及它们的优缺点,如何准确判断数组类型
- 可能发生隐式类型转换的场景以及转换原则,应如何避免或巧妙应用
- 出现小数精度丢失的原因,JavaScript可以存储的最大数字、最大安全数字,JavaScript处理大数字的方法,避免精度丢失的方法