JavaScript 程序设计——进阶篇
1.类型进阶
类型
原始(值)类型:Undefined Null Boolean String Number Object
对象(引用)类型:Object
通过 JS 语言操作的所有东西几乎都是 Object 类型的对象。
对象类型:分为浏览器扩展对象,宿主对象和原生对象。原生对象分为构造函数和对象。
构造函数:Boolean String Number Object Function Array Date RegExp Error
对象:Math JSON 全局对象 arguments
原始类型和对象类型的区别:
所有的原始类型的值存在栈内存中,可直接用变量标识符取到它的值并为它赋值。
对象类型的栈内存中只存储地址,实际的值存储在堆内存中,地址指向堆内存中的值。对象类型赋值时仅赋予地址值。
隐式类型转换
JS 是一门弱类型语言,表示语言本身会做一系列的隐式类型转换。
数学运算符:
+ 操作:一端是字符串,一端是数字时,数字被隐式转换为字符串,实现字符串连接操作。
其他数学运算符:一端是字符串,一端是数字时,字符串被隐式转换为数字进行数值计算。
. 操作:所有的直接量用 “.” 调用某个方法时,JS 运行环境会将直接量转换为对应的对象类型,调用对象类型上的方法实现功能。
if 语句:条件部分的表达式结果隐式转换为布尔值进行判别。
==:在做 == 操作时会进行隐式类型转换。
显式类型转换
在隐式类型转化不满足需求时,需要进行显式类型转换。
Number() String() Boolean()
parseInt() parseFloat() 字符串转换为数字
! !! 取得操作对象的布尔值
类型识别
typeof:可以识别标准类型(Null 除外),不能识别具体的对象类型(function除外)。
instanceof:判别内置对象类型,自定义对象类型(及父子类型)。不能判别标准类型。
Object.prototype.toString.call():可以识别标准类型以及内置对象类型,不能识别自定义类型。
constructor:识别标准类型(null,unefined 除外),内置对象类型,自定义类型。
2.函数进阶
函数定义:函数声明,函数表达式,函数实例化。
函数声明定义函数的特点:函数定义会被前置;重复定义函数时,最后一次定义有效。
函数实例化定义函数的特点:定义的函数只能访问本地作用域和全局作用域。
JS 代码执行过程:在 JS 代码执行之前,JS 引擎首先会对需要执行的 JS 代码做一次预解析,将一些变量声明,函数定义提前处理,然后再单步执行 JS 代码。
函数调用
函数调用模式:add(1);
方法调用模式(函数作为对象属性):myNumber.add(1);
构造函数调用模式:new Function(...);
apply(call) 调用模式:为Function构造函数原型链中一个方法,实现函数调用功能,将函数借用给一个对象,帮助它实现函数所定义的逻辑。
Function.prototype.apply()的使用函数执行时,JS 引擎会在函数自己的本地作用域上自动添加 this,arguments 这两个临时变量,调用模式的区别体现在 this 变量的指向上。
函数调用模式的区别——this:
函数调用模式:this 指向全局对象
方法调用模式:this 指向调用者
构造函数调用模式:this 指向被构造的对象
apply(call)调用模式:this 指向第一个参数(借用方法的对象)
arguments:用于获取函数实参的对象,类似数组的对象。arguments[index] 获取实参,arguments.length 获取函数实参个数。
函数传参
按值传递(call by value):一个外部变量传递给一个函数时,函数实参获取到的是外部变量的一个副本,在函数内部对实参进行的修改不会反映到外部变量上。
按引用传递(call by reference):一个外部变量传递给一个函数时,函数实参是外部变量的一个引用,在函数内部对实参的任何修改都会反映到外部变量上。
按共享传递(call by sharing):一个外部变量传递给一个函数时,函数实参获取到的是外部变量的一个副本,在函数内部对实参进行赋值不会反映到外部变量上,修改实参对象的属性值,会反映到外部变量上。
原始类型按值传递,对象类型按共享传递。
闭包
函数内部的子函数用到了父函数的变量形成的特定作用域。
功能:保存函数的执行状态,封装,性能优化。
保存函数的执行状态 封装,隐藏status,light对象属性 性能优化:函数在闭包作用域内,减少函数定义时间和内存消耗first-class function
JS 当中的函数可以被当作普通变量所使用,可被当作参数,也可被当作返回值返回。
功能:函数作为参数(异步回调函数);函数作为参数(curry)
函数柯里化通常是指把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的并且返回一个接受余下的参数而且返回结果的新函数的技术。
更泛化的定义是指给函数分步传递参数,每次函数接受部分参数后应用这些参数,并返回一个函数接受剩下的参数,这中间可嵌套多层这样的接受部分参数的函数,直至返回最后结果。归纳一下就是逐步传参,逐步缩小函数的适用范围,逐步求解的过程。
从更上层的角度去理解,柯里化允许和鼓励你将一个复杂过程分割成一个个更小的更容易分析的过程(这些小的逻辑单元将更容易被理解和测试),最后这样一个难于理解复杂的过程将变成一个个小的逻辑简单的过程的组合。
function.prototype.bind
bind 返回的是一个函数引用,而不执行。调用函数引用才执行绑定的函数。
3.原型
原型 vs 类:
类构造对象的方式是一个从抽象到具体的过程(new 关键字构造),类是具体事物的抽象,对象是具体的。
原型构造对象的方式是从具体到具体的过程。使用现有的对象去构造一个新的对象。
原型构造对象方式:Object.create 构造函数
Object.creat(proto):proto 为一个对象,作为新创建对象的原型。
var landWind = Object.create(landRover);
被创建的对象有一个内置属性 _proto_,指向对象的原型。
构造函数:使用 prototype 设置原型,使用 new 创建对象。
每个函数有 prototype 属性,为一个指针,指向一个原型对象(通过该构造函数创建实例对象的原型对象)。实例 ——> 原型对象 <—— 构造函数
new 的过程拆分为三步:创建对象;设置被创建对象的 _proto_ 指向原型对象;apply 实现函数借用功能。
原型链
网页截图对象属性的访问:优先在对象本身查找,对象本身定义顺着原型链查找。
对象属性的修改,删除:只能修改,删除对象自身的属性,不影响其他对象。
JS 里所有对象,背后都有原型链存在。
对象.hasOwnProperty(属性) 判断传入属性是不是对象自身的属性,是返回 true,否则返回 false。
4.变量作用域
JS 使用静态作用域。由程序定义的位置决定。在编译阶段决定,与代码执行顺序无关。一般采用嵌套作用域的规则解析。
JS 没有块级作用域(全局作用域,函数作用域)
使用词法环境管理静态作用域
词法环境
描述环境的对象,包含环境记录(环境里定义的形参、函数声明、变量。),对外部词法环境的引用(outer)。
一段JS代码执行之前,会对环境记录进行初始化(声明提前),即将函数的形参、函数声明和变量先放入函数的环境记录中,特别需要注意的是:形参会在初始化的时候定义值,但是函数内部定义的变量只声明不定义(不赋值)。
词法环境——with,try-catch 函数
以 with 为例,在 JS 里面会创造一个临时的词法环境,传入的参数定义到 with 的环境记录中。但在 with 中定义的变量,函数声明为全局作用域。匿名函数的 outer 指向 with 词法环境。
函数表达式和函数声明的作用域
函数表达式在执行时改变作用域,函数声明只与定义时的位置有关。
带名称的函数表达式作用域
执行时创建新的词法环境,不会定义到全局。名称不能修改。
5.闭包
闭包由函数及其相关的引用环境的组合而成。
闭包允许函数访问其引用环境中的变量(又称自由变量)。
广义上来说,所有 JS 的函数都可以成为闭包,因为 JS 函数在创建时保存了当前的词法环境。
闭包的应用:保存现场,封装。
6.面向对象
全局变量:可以在程序的任何地方访问。
var test = 'some value'; 无法用 delete 删除
window.test = ‘some value’;
(function(){var a; test = 'some value';})(); 写了变量赋值但未用 var 定义。
后两种方法将变量定义在 window 对象上面,可使用 delete 删除。
封装形式
私有的方法和属性供对象本身调用继承