《JavaScript设计模式与开发实践》
JavaScript的根对象是Object.prototype对象——一个空对象,JS中每个对象都来自它的克隆,Object.prototype就是它们的原型对象,所有对象都可追溯到它
通过克隆Object.prototype来得到新的对象
function Person(name){
this.name= name;
}
var a = new Person(‘sven’);
JS没有类,new Person这里是函数构造器,JS的函数可做普通函数被调用,也可做构造器被调用
1.1、JavaScript中的原型继承
javascript中没有类,遵从原型编程的基本规则
1、数据都是对象 2、对象是通过找到另一个对象作为原型并克隆它
3、对象会记住它的原型
准确来说对象没有原型,而是构造器有原型,说的是构造器的原型
4、如果对象无法响应某个请求,它会把这个请求委托给它自己的构造器原型
对象有隐藏属性-proto- 默认指向它的构造器的原型对象。
四、运算符
!==可以区分null、undefined
五、语句
for/in循环可以遍历对象中所有可枚举的属性(包括自有属性和继承属性),对象继承的内置方法不可枚举
六、对象
对象(数组、函数)
除了不可变的原始值外(字符串、数字、布尔、null、undefined)外,JS中的值都是对象,字符串、数字、布尔值行为和不可变对象非常类似
JS为方便描述,人为划分三类对象和两类属性
内置对象:由ECMScript规范定义的对象或类,数组、函数、日期、正则表达式都是内置对象
宿主对象:由JS解释器所嵌入的宿主环境(比如web浏览器)定义的。客户端js中表示网页结构的HTMLElement对象均是宿主对象。既然宿主环境定义的方法可以当做普通的Js函数对象,那么宿主对象也可以当成内置对象。
自定义对象:在运行中创建的JS对象
own property:直接在对象中定义的属性
inherited property :对象的原型对象中定义的属性
6.1创建对象
方式:对象直接量、new、Object.create()函数
6.1.1原型
Object为Object也代表具体对象
Object.prototype获得原型对象引用,例如:通过new Array()创建的对象原型为Array.prototype
[注]没有原型的对象不多,其中Object.prototype没有原型对象
6.1.2、Object.create通过任意原型创建对象
Object.create(),创建一个新对象,param1——是这个对象的原型,param2——对这个对象的属性描述
var o1 = Object.create({x:1,y:2}); //o1继承了x,y
如何创建空对象
var 02 = Object.create(Object.prototype)、new Object、{}
6.1.3 作为关联数组的对象
object.property==object[“property”],后者更像数组,这个数组元素是通过字符串而不是数字访问。这种数组就是关联数组,JS对象都是关联数组,也叫散列、映射、字典。
6.14检测属性
in、hasOwnPreperty()、propertyIsEnumerable()
in —检测自有和继承属性
hasOwnPreperty–检测自有
propertyIsEnumerable----检测自有且可枚举
6.2
Object.keys()、Object.getOwnPropertyName()方法
6.3 属性:名字、值、attribute组成。
数据属性4特性:value\writable\enumrealbe\configurable
存取器属性4特性:get\set\enumrealbe、configurable
[主要概念]1、getter\setter2、存取性属性3、定义对象O的存取器属性accessor_prop
定义:
var o = {
data_prop : value,
get accessor_prop(){函数体},
set accessor_prop(value){函数体}
}
6.3.1”属性描述符“对象
用来代表4特性,该对象的属性和它们所描述的属性特性同名。
Object.getOwnPropertyDescriptor()可以获得对象的属性描述符。
Object.definedProperty(),用来创建或修改属性以及描述符对象
Object.definedProperties(),
6.4对象三属性
每一个对象都有三属性:prototype,class,extensible attribute
[使用]
6.4.1prototype属性
Object.getPrototypeOf(o),可查o的原型,
检测一个对象是否是另一个对象的原型(或处于原型链)------isProtorypeOf()
6.4.2class属性
是一个字符串
只有间接方法查询到它 toString()方法(继承自Object.prototype) 返回[object class] 再提取第8个到倒数第二个之间的字符
不过很多对象重写了toString(),为了正确得到toString()版本必须间接调用Function.call()方法
【注】通过对象直接量和Object.creat(),和自定义构造函数创建的对象的类属性是"Object"
内置构造函数与名字匹配
6.4.3 extensible:目的是“锁定”,避免外界干扰
对象的可扩展性用于表示是否可以给对象添加新属性。内置对象和自定义对象都是显示可扩展,宿主对象的可扩展性是由JS引擎定义的。
使用:
Object.esExtensible§:用来判断p是否可扩展
Object.preventExtensions():对象————————》不可扩展,一旦对象转换为不可扩展就不可逆。
Object.seal(),Object.isSealed(),
Object.freeze(),Object.Isfrozen().
说明:preventExtensions、seal、freeze都返回传入的对象,也就是说可以通过函数嵌套的方式调用它们
var o = Object.seal(Object.creat(Object.freeze({x:1}),{y:{value:2,writable:true}}));
6.5序列化对象
对象序列化是指将对象的状态转换为字符串,也可将字符串还原为对象
ECSScript5 提供内置对象JSON.stringify() 和JSON…parse()用来序列化和还原JS对象。
【特别说明】stringify只能序列化对象的可枚举的自有属性。两者都接受第二参数来定制操作
JSON 是JS语法的子集,并不能表示JS里面所有的值,
支持:对象、数组、字符串、无穷大数组、布尔、null
特殊:NaN、Infinity、-Infinity 序列化结果为 null
特殊:Date对象的序列化结果为 ISO格式的日期字符串(参照Date.toJSON()函数),但JSON.parse()依然保留字符串格式,不会转为Date对象
不支持:函数、RegExp,Error对象、undefunded。
6.6对象方法(Object.prototype)
6.6.1toString()
返回:一个表示调用这个方法的对象值得字符串
什么时候调用:在需要将对象转为字符串的时候。
由于默认toString()返回信息太少,很多类都有自定义toString()方法
6.6.2 toLacalString()
返回:表示这个对象的本地化字符串
Object中默认的toLacalString只是调用toString,Date、Number类对toLacalString做了定制,可以用它对数字、日期做本地化转化,Array.toLacalString()是调用每个数组元素的toLacalString
6.6.3 toJSON()
object.prototype,没有此方法,对于需要序列化的对象来说,JSION.stringify()会调用toJSON()方法,如果待序列化的对象存在该方法,则调用它
6.6.4 valueOf()
什么时候用:需要将对象转为原始值而非字符串时候,尤其转为数字的时候。
有些类自定义了valueOf,比如Date.valueOf();
七、数组(2^32-2)
数组继承Array.prototype中的属性,它定义了一套丰富的数组操作方法。
【数组索引本质】数组是对象的特殊形式,适用方括号访问数组元素就像用方括号访问对象的属性一样。JS将指定的数字索引转换为字符串,然后将其作为属性名使用。故数组本质上其索引也是属性值。
索引特点:可以负数或非整数来索引数组,数字转为字符串,字符串作为属性名
例:a[-1.23] = true;a[23.4]=a;
创建:直接量、构造函数new Arrray().
当使用小于2^32的非负整数 ,Array自动更新length值。
7.1 数组元素的添加和删除
添加:索引、push()、unshift()
删除:delete、pop()、shift()
通用方法 :splice()
遍历的新方法:forEach()
7.2 数组方法
7.2.1 join()—将数组元素拼接在一起
reverse()-----反转元素
sort() 排序,可接受排序规则函数
concat()—创建并返回新数组
slice()—返回数组片段
splice()—插入或删除元素,或同时进行两种操作,返回由删除元素组成的数组。
splice(i,j,…)-------i、j定义删除的元素,i以及之后元素定义插入元素
splice(1)删除1以及之后所有元素,
splice(1,2),删除1以及以后2个元素
splice(1,0,[2,3],6);
push()、pop() 返回长度
unshift()、shift()返回元素
7.2.2 ECMScript5 定义的数组方法
foreach(),调用函数传入一个参数,代表value,传入三个参数代表,value,索引,数组。
如果只关心数组元素值可以编写只有一个参数的函数
map()调用数组的每个元素传递给指定函数,该函数应该有返回值,返回的数组包含所有返回值。
filter()返回数组子集,传入函数是用来逻辑判定的,返回值是true或false
every()、some() 是数组的逻辑判定,它们对数组元素用指定函数进行判定,返回true或false
reduce()|reduceRight() 使用指定函数将数组元素进行组合,生成单个值。区别是从左到右和从右到左
indexOf()、lastIndexOf()搜索整个数组具有给定值的元素,接受两个参数(v,j)j可选,表示偏移量
7.3数组类型
思考:除了isArray()方法还能怎样区分数组和非数组
数组类型判定:Array.isArray()
7.4类数组对象
思考:数组对象有哪些特性,普通对象没有的。
定义:把拥有一个数值length属性和对应非负整数属性的对象看做一种类型的数组
var a = {“0”:“a”,“1”,“b”,“2”:“c”,length:3}
7.5 字符串另一个身份——作为只读数组
数组的不改变值的一些操作方法适用于字符串
八、函数
this关键字:
任何函数只要作为方法调用实际上都会传入一个隐形实参对象this----这个对象就是方法调用的母体。
rect.setSize(width,height);
setRectSize(rect,wight,height);
假定两行代码功能完全相同,他们都作用于一个假定的rect对象,而第一行简明清晰表明了函数执行的载体是rect对象,函数中所有的操作都将基于这个对象。
JavaScript中函数也是对象,是特殊对象,可以定义变量和函数属性(即函数多重嵌套函数)
8.1 函数定义
两种方式:表达式,语句
【定义后立马使用】()为运算符,(function)为立马使用函数
1、var a = function(a){ console.log(a)} 定义
2、(function(a){ console.log(a)}(success) ) 定义后立马使用,只是用一次的可以这样定义为匿名函数
8.2 函数调用
4种方式:作为自身独立的函数;对象方法;构造函数;通过call()和apply()
【注意】t和变量不同,this没有作用域的限制,嵌套函数作为方法调用,其this值就指向调用它的对象,作为函数调用,其this值不是全局对象(非严格模式)就是undefined(严格模式)
8.3 间接调用
函数作为对象也包含call()和apply()方法,两个方法可以间接调用函数。
两个方法都允许显示指定调用所需的this值,也就是说,任何函数都可以作为任何对象的方法来调用。都可以指定实参,call()使用自有的实参列表作为函数的实参,apply()要以数组的形式传入参数
8.4 实参对象
8.4.1 arguments 指向实参对象
8.4.2 callee、caller 属性
callee属性指代当前正在执行的函数,caller指代调用正在执行函数的函数
用处例如:callee属性 实现递归
var factotial = function(x){ if(x<=1) return 1; return x*arguments.callee(x-1)}
8.4.3 传入整个对象------将对象属性作为实参
8.4 函数也是值
函数不仅仅是语法,也是值,可以将函数赋值给变量,存储在对象属性或数组元素中,作为参数传入另一个函数等
8.5 闭包
作用域、作用域链
闭包概念:函数对象可以通过作用域链关联起来,函数体内部的变量都可以保存在函数作用域内的特性叫做闭包。闭包是一种特性。
8.6 函数的属性,方法和构造函数
两个属性,两个方法
length属性:arguments.length代表实参个数,length代表函数的定义时候的形参个数
propertyprototype属性:指向一个原型对象的引用
借鸡生蛋,call()方法,和apply()方法【可以方便实现,函数的四处调用,即使不是该对象方法,也可以由该对象调用】
call(o,arguments),第一个参数都是传入上下文对象,不同的是,call传入有限个实参,apply()需要传入数组。
bind()方法 ,将函数绑定至对象
toString()方法
Function()构造函数,相当于eval(),万万切记,Function构造函数编译只能看到全局属性,函数内属性无法访问
8.7 函数式编程
JS不是函数式编程语言,但可以像操作对象一样操控函数,也就是说可以在JS中应用函数式编程技术。请看下面的应用
8.7.1 使用函数处理数组
8.7.2 高阶函数
高阶函数就是操纵函数的函数,他接收多个函数作为参数并返回一个新函数
8.7.3 不完全函数,不完全调用(一种函数变换技巧,把一次完整的函数调用拆成多次调用,每次调用都返回一个函数)
例如:f(1,2,3,4)拆成f(1,2)(3,4)
8.7.4 记忆
一种编程技巧,在函数式编程中把上次计算结果缓存起来,本质就是空间换时间
九、类和原型
1、类、原型、构造函数:原型对象是类的唯一标识,任何js函数都可以作为构造函数,每个js函数都自动拥有一个prototype属性,这个属性值是一个对象,这个对象包含一个不可枚举属性constructor。constructor属性的值是一个函数对象。
当指定Range.prototype原型,对Range()构造函数的调用会自动使用Range.prototype作为新Range对象的原型。
2、constructor属性:就是一个函数对象
3.JS中的Java式的类继承
4、类的扩充
可以通过给原型对象增加方法扩充JS类
JS内置类的原型对象也是“开放的”,可以给数字,字符串、数组、函数等数据类型添加方法
5、类和类型
提问:对于"基本类型",检测其类型可以用typeof,但是对于类如何区分(如何让类告诉你它的名字)?
检测对象类的技术:instanceof运算符,constructor属性,构造函数名字。
instanceof或者isPrototypeOf,无法让类说出它的名字。
constructor识别对象属于某个类(它说出了名字)。
构造函数名称。
6、JS中的面向对象的技术
方法借用:
JS中的“多重继承”通过“方法借用来实现”,即多个类中的方法共用一个单独函数,
私有状态----------类比 private
实现:将变量(或参数)闭包在一个构造函数内来模拟实现私有字段。