《JavaScript高级程序设计》知识点小结 II
ch5 引用类型
1 Object类型
创建Object实例
var person = new Object();
var person = {};
访问对象属性
点表示法 person.age;
方括号表示法 person[“age”] 优点:可以通过变量来访问属性
2 Array类型
特点:每一项可以保存不同类型的数据;大小可以动态增长。
创建数组
- Array构造函数
var colors = new Array(3);
var colors = new Array(“yellow”,“red”);
new可以省略。 - 数组字面量表示法
var colors = [“red”,“blue”];
读取和设置数组的值
使用方括号和基于0的数字索引
length属性
获取数组的项数。不是只读的,可修改。
用于添加新项:colors[colors.length]=“black”;
检测数组
经典问题之确定某个对象是不是数组
对于一个全局作用域而言,value instanceof Array即可,但是对于一个frame以上的网页,不同框架之间原生创建的数组具有不同的构造函数。
新增方法:Array.isArray(value)。
转换方法
使用对象自带的方法:
- valueOf():对数组的每一项调用toString()方法,然后返回数组。
- toString():返回由数组中每个值拼接而成的一个以逗号分隔的字符串。
- toLocalString():不同之处在于,对每一项调用的函数时toLocalString()。
- join()方法:返回使用用户定义的分隔符来构建的字符串。
栈方法
- push():接受任意数量的参数,并逐个添加到数组末尾。
- pop():从数组末尾删除最后一项,减少数组length的值,然后返回移除的值。
队列方法
- shift():移除数组的第一项并返回该项。
- unshift():在数组前端添加任意个项,并返回新数组的长度。
重排序方法
- reverse():反转数组项的顺序。
- sort():按照升序排列数组。可以接受一个比较函数作为参数。
操作方法
- concat():接受的参数表示要像数组中新增的项。
- slice():接受两个参数,表示截取的开头和结尾,[),包括头但不包括。返回截取的数组,对原数组不做改动。
- splice():接受2个参数及以上。第一个参数表示开始操作的位置,第二个参数表示要删除的项数,第三个参数开始表示要插入的内容。返回值为存放着被删除的项的数组,若无删除项,返回空数组。
位置方法
- indexOf()
- lastIndexOf()
迭代方法-5个
都不会修改数组原来的值。两个参数:函数;作用域对象(影响this的值)。
函数会收到三个参数:数组项的值,该项索引,数组对象本身。
首先对每一项运行函数,然后进行不同的操作。
- every():若均为true,返回true。
- filter():返回由true项组成的数组。
- forEach():无返回值。
- map():返回每一项结果组成的数组。
- some():若任一项返回true,则返回true。
例:numbers.forEach(function(item,index,array){});
缩小方法
接受两个参数:函数;作为缩小基础的初始值。
传入的函数会收到四个参数:前一个的返回值,当前值,当前索引值,数组对象本身。
- reduce():从数组的第一项开始,逐个遍历到最后。
- reduceRight():从最后一项开始。
例:var nums=[1,2,3,4];
nums.reduce(function(pre,value,index,array){return pre+value;},10);
返回值为20
3 Date类型
创建日期对象
- new Date()
var now = new Date();
基于本地时区来构建而不是GMT。 - Date.parse(x)
该方法接受一个表示日期的字符串参数,然后尝试返回毫秒数。 - Date.UTC()
返回的时间是基于GMT的。返回毫秒数。
参数:年份、基于0的月份、月中的哪一天、小时数(0-23)、分钟、秒及毫秒数。只有前两个参数是必须的。天数默认为1,其他默认为0。
继承的方法
- toLocalString() “2020/4/4”
toString() “Sat Apr 04 2020 14:00:00 GMT+0800 (中国标准时间)”
日期格式化方法
一些专门用于将日期格式化为字符串的方法:
- toDateString() “Sat Apr 04 2020”
- toTimeString() “14:00:00 GMT+0800 (中国标准时间)”
- toLocalDateString() “2020/4/4”
- toLocalTimeString() “下午2:00:00”
- toUTCString() “Sat, 04 Apr 2020 06:00:00 GMT”
日期/时间组件方法
- 前缀1:get 获取 / set 设置
- 前缀2:UTC (世界统一时间)/ 无
- 前缀3:Full 四位数年份 / 无(其它都为无)
- 前缀4:Time 毫秒 / Year 年份 / Month 月份 / Date 日子 / Day 星期几 / Hours 小时(0-23) / Minutes 分钟数(0-59) / Seconds 秒数(0-59) / Milliseconds 毫秒
4 RegExp类型
创建正则表达式
var expression = / pattern / flags;
字面量表示你真正要表达的东西。
RegExp属性
- global:g全局匹配
- ignoreCase:i忽略大小写
- lastIndex:表示开始搜索下一个匹配项的字符位置
- multiline:m多行
- source:正则表达式的字符串表示
RegExp方法
-
exec()
接受一个参数,要匹配的字符串。返回包含第一个匹配项信息的数组,还附带两个属性,input和index。 -
test()
接受一个字符串参数。匹配时返回true,否则返回false。
RegExp构造函数属性
- RegExp.input/RegExp.$_ 最近一次要匹配的字符串
- RegExp.lastMatch/RegExp.$& 最近一次的匹配项
- RegExp.lastParen/RegExp.$+ 最近一次匹配的捕获组
- RegExp.leftContext/RegExp.$` input字符串中lastMatch之前的文本?
- RegExp.multiline/RegExp.$* 是否多行
- RegExp.rightConetxt/RegExp.$’ 字符串中lastMatch之后的文本
常用正则表达式
^[0-9]*$ 数字
^\d{n}$ n位的数字
^\d{m,n}$ m-n位的数字
^(0|[1-9][ 0-9]*)$ 零和非零开头的数字
^([1-9][0-9]*)+(.[0-9]{1,2})?$ 非零开头的最多戴两位小数的数字
^(\-)?\d+(\.\d{1,2})$ 带1-2位小数的正数或负数
5 Function类型
定义函数
- 使用函数声明语法定义
function sum(num1,num2){return num1+num2;}
解析器会先读取,在其执行任何代码之前可用。
有函数声明提升。(function declaration hoisting) - 使用函数表达式
var sum=function(num1,num2){return num1+num2;}
必须等到解析器执行到它所在的代码行,才会真正被解释执行。 - 使用Function构造函数
var sum=new Function(“num1”,“num2”,“return num1+num2;”);(不推荐,会导致解析两次,影响性能)
没有重载
后面定义的函数会覆盖前面的。本质相当于变量的重新赋值。
作为参数或返回值的函数
作为返回值:可以生成具有某些可动态生成的函数。
函数内部属性
- arguments.callee() 相当于原函数
一个指针,指向拥有这个arguments的函数。这解除了函数体内的代码与函数名的耦合状态。 - this对象
- functionName.caller
保存着调用当前函数的函数的引用。
函数属性和方法
- length属性
表述函数希望接受的命令参数的个数。 - prototype属性
保存函数的所有实例方法。
用于设置函数体内this的值,“它们真正强大的地方在于能扩充函数运行的作用域。” - apply()
两个参数:一个是运行在其中的作用域,另一个是参数数组,array实例或者arguments对象均可。 - call()
第一个参数是this绑定的对象,剩下的均作为参数传入。
apply和call的区别,主要看传入的参数应该用什么形式传入。 - bind()
该方法创建一个函数的实例,其this值会绑定给传入的参数。
6 基本包装类型
Boolean、Number、String类型
当读取一个基本类型值的时候,后台会创建一个对应的基本包装类型的对象从而让我们能够调用一些方法来操作这些数据。
基本包装类型值存在于一行代码的执行瞬间,因此不能在运行时添加属性和方法。
可以显示地调用Boolean、Number、String来创建基本包装类型的对象。typeof会返回Object,转化为布尔值时为true。
6.1 Boolean类型
在表达式中使用Boolean对象时,返回true,因为是对象。
建议不要使用。
6.2 Number类型
- toFixed() 参数:小数位数
- toExponential() 参数:几位小数
- toPrecision() 选择上述两种方法中合适的一种。
还是不推荐使用。
6.3 String类型
字符方法
- charAt()
- charCodeAt()
字符串方法
- concat()
- slice() 参数:开始,结束
- substr() 参数:开始,个数
- substring() 参数:开始,结束
若没有第二个参数则到结尾位置。
当参数为负数时,slice会将负值与字符串长度相加,而substr第一个会相加,第二个会转为0;substring会将所有都转为0。
位置方法
- indexOf()
- lastIndexOf()
没找到则返回-1
trim方法
- trim()
大小写转换方法
- toLowerCase()
- toLocaleUpperCase()
- toLowerCase()
- toLocaleUpperCase()
模式匹配方法
- match()
一个参数,RegExp对象或者正则表达式。返回一个数组。 - search()
返回第一个匹配项的索引。若没有,返回-1。 - replace()
接受两个参数,用参数2替换参数1。参数1可以是字符串,也可以是正则表达式,带g标志的正则表达式可进行全局匹配并替换。
第二个参数可以包含字符序列
$$ $
$& 匹配整个模式的子字符串
$' 匹配的字符串之前的字符串
$` 匹配的字符串之后的字符串
$n 匹配第n个捕获组的子字符串
$nn 同上
第二个参数也可以是函数。向函数传递三个参数:模式的匹配项,在字符串中的位置,原始字符串。
function(match,pos,originalText){}
例:function htmlEscape(text){
return text.replace(/[<>"&]/g,function(match,pos,originalText){
switch(match){
case "<":
return "<";
case ">":
return ">";
case "&":
return "&";
case "\":
return """;
}
})
}
- split()
基于指定的分隔符将一个字符串分割成多个字符串,并将结果放到一个数组中。
第一个参数可以是字符串,也可以是正则表达式。
第二个参数可以用于指定结果数组的大小,也可以不填。
localeCompare()方法
“小于”返回-1,相等返回0,“大于”返回1.
构造函数的方法
- String.formCharCode()
接受一个或多个字符编码,然后将他们转化为字符串。
7 内置单体对象
7.1 Global对象
“兜底儿对象”
URI编码方法
- encodeURI()
不会对本身属于URI的特殊字符进行编码。
对应解码 decodeURI() - encodeURIComponent()
会对它发现的任何非法字符进行编码。
对应解码 decodeURIComponent()
eval()方法
- eavl()
只接受一个参数,即要支持的js字符串。
其中任何变量和函数都不会被提升,因为在解析代码的时候,他们被包含在一个字符串中,只有在eval()被执行的时候才创建。
Global对象的属性
undefined NaN Infinity Object Array Function Boolean String Number Date RegExp Error EvalError RangeError RefenceError SyntatError TypeError URIError
window对象
7.2 Math对象
属性
- Math.E
- Math.LN10
- Math.LN2
- Math.LOG2E
- Math.LOG10E
- Math.PI
- Math.SQRT1_2 1/2的平方根
- Math.SQRT 2的平方根
min()和max()方法
用于确定一组数值中的最大值和最小值。
舍入方法
- Math.ceil()
- Math.floor()
- Math.round() 注意负数舍入。Math.round(-10.5)输出 -10
生成随机数方法
- random()
返回介于0和1之间的随机数,不包括0和1。 - selectFrom()
两个参数,应该返回的最大值和最小值。
其他方法
Math.abs(num)
Math.exp(num)
Math.log(num)
Math.pow(num,power)
Math.sqrt(num)
Math.acos(x) 反余弦值
Math.asin(x)
Math.atan(x)
Math.atan2(y,x)
Math.cos(x)
Math.sin(x)
Math.tan(x)
ch6 面向对象的程序设计
1 理解对象
属性类型
js中有两种属性:数据属性和访问器属性。
数据属性
四个特性:configurable(能否删除、修改属性特性,默认true) enumerable(能否用for-in循环遍历,默认true) writable(value是否可改,,默认true) value(值)
- Object.defineProperty()方法
三个参数:属性所在的对象,属性的名字和一个描述符对象。
var person={};
Object.defineProperty(person,"name",{
writable:false;
value:"Nike";
})
访问器属性
不包含数据值,但是包含一对getter和setter函数。
四个特性:configurable enumerable get set
var book={
_year:2019;
edition:1;
}
Object.defineProperty(book,"year",{
get:function(){
return this._year;
},
set:function(newValue){
if(newValue>2019){
this._year=newValue;
this.edition += newValue - 2019;
}
}
})
book.year=2020;
alert(book.edition) // 2
定义多个属性
- Object.defineProperties()方法
两个对象参数:第一个是要被修改和添加属性的对象,第二个是要添加或修改的属性。
var book={};
Object.defineProperties(book,{
year:{
value:2020;
},
edition:{
value:2;
}
})
读取属性的特性
- Object.getOwnPropertyDescriptor()
两个参数:属性所在的对象,属性名称。根据返回值descriptor来访问,比如descriptor.value,decriptor.enumerable
2 创建对象
2.1 工厂模式
函数根据传入的参数,显式new一个Object,并将参数设置到对象中,最后返回该对象。
问题:无法识别对象类型。
2.2 构造函数模式
使用自定义的构造函数,在该函数中直接将方法和属性赋值给this对象。
按照惯例,构造函数以大写字母开头。
使用
使用new来调用,与普通函数的区别只在于调用方式。
如果直接调用,this会指向不同的对象。在全局中将指向global对象,在浏览器中则是window对象。
问题
定义方法时,每一个实例都重新定义了同名方法,这完全是没必要的。不同实例上的同名函数是不相等的。
若将函数移到全局,可以减少重复定义,但这破坏了封装性。所以参考↓原型模式。
2.3 原型模式
理解原型对象
区分好这三个概念:
构造函数(Person),含有prototype属性,指向原型对象。
原型对象(Person.prototype),自动获得一个constructor属性,这个属性指向构造函数。
实例(person1. __ proto __),含有一个指针 [ [ prototype ] ] ,浏览器中定义了一个__proto__属性,执行原型对象。
-
isPrototype()
确定一个对象与一个原型是否有关系
Person.isPrototype(person1)//true -
Object.getPrototypeOf()
获取原型对象
Object.getPrototypeOf(person1)==Person.prototype -
hasOwnProperty()
检测实例属性。
只有属性存在于实例中,才返回true。 -
属性的读取顺序
首先在实例内搜索,找不到的话才去原型中寻找。
可以重名,只屏蔽而非重写。
可以使用delete删除实例属性。
原型与in操作符
- in
能访问到的实例或者原型属性,会返回true。
用于确定是否原型属性的函数
function hasPrototypeProperty(object,name){
return !object.hasOwnProperty(name)&&(name in object);
}
-
for-in
枚举所有enumerable的属性,包括原型属性和实例属性。 -
Object.keys()方法
获取所有可枚举属性,接受一个对象作为参数,返回一个包含所有可枚举属性的字符串数组。 -
Object.getOwnPropertyNames()
获取所有属性(包括不可枚举)
简化原型语法
可以用对象字面量来写原型Person.prototype。
注意此时的constructor属性指向Object,无法确定其类型。可以显式地将属性设置为适当的值,如:constructor:Person;
此时constructor是可以枚举的,所以还应该使用Object.definePropoty()
Object.defineProperty(Person.prototype,"constructor",{
enumerable:false;
value:Person;
})
原型的动态性
一般而言,对原型对象所做的任何修改都能直接从实例上反映出来。
但是如果重写整个原型,会切断构造函数与最初原型之间的联系,因为调用构造函数的时候,会给实例添加一个指向原型的 [ [ prototype] ],重写整个原型就相当于将原型修改为另外一个对象。也就是说,重写原型之前构造的实例,访问不到重写之后的原型。
原生对象的原型
原生的引用类型,也是这样创建的。可以给原生对象的原型,添加属性方法。
String.prototype.startsWith=function(text){
return this.indexOf(text)==0;
}
问题
原型模式最大的问题都是其共享的本性所导致的。
所以一般和构造函数结合着使用↓。
2.4 组合构造函数与原型模式
最常见的方式。
function Person(name,age){
this.name=name;
this.age=age;
}
Person.prototype={
constructor:Person;
sayName:function(){
alert(this.name);
}
}
2.5 动态原型模式
实现封装。“这种方法非常完美。”
动态就是指,根据条件判断的结果,来决定是否向原型中添加方法。
在构造函数内部:
if(typeof this.sayName != "function"){
Person.prototype.sayName=function(){
alert(this.name;)
}
}
if语句控制函数的添加只实现一次。
同样不能用对象字面量重写原型。
*2.6 寄生构造函数模式
思想
创建一个函数,该函数的作用仅仅是封装创建对象的代码,然后再返回新创建的对象。
除了使用new操作符并把使用的包装函数叫做构造函数之外 ,其实和工厂函数是一模一样滴。
使用:new 工厂函数
例:“想创建一个具有额外方法的特殊数组”
function SpecialArray(){
var values=new Array();
values.push.apply(values,arguments);
values.toPipedString = function(){
return this.join(",");
}
return values;
}
*2.7 稳妥构造函数模式
没有公共函数,其方法也不引用this的对象
3 继承
3.1 原型链
让原型对象等于另一个类型的实例。
构造函数、原型和实例的关系:每个构造函数都有一个原型对象,原型对象包含一个指向构造函数的指针(constructor),而实例都包含了一个指向原型对象的内部指针。
实现原型链
创建父亲实例,并将父亲赋值给儿子的原型而实现的。本质是重写儿子的原型对象,代之以父亲的实例。
原型链中的搜索机制:一环一环地搜索,直到原型链末端才停下来。
默认的原型
Object.prototype 这也是所有自定义类型都会继承toString(),valueOf()等默认方法的根本原因。
确定原型的类型
只要是原型链中出现过的原型,均可以是其类型。
判断方法:instanceof;原型.isPrototypeOf()
谨慎地定义方法
给原型添加方法的代码一定要放在替换原型的语句之后。
不能用对象字面量创建原型方法,否则会切断原型链。
问题
父亲的属性值容易被儿子修改,而修改之后所有的儿子都会受到影响。
实践中很少会直接使用原型链。
3.2 借用构造函数
也叫伪造对象、经典继承
思想:在子类构造函数的内部调用父类构造函数。
构造父亲的时候传递参数,使得每一个子类可以拥有自己想要的父亲。
实现
在子类构造函数中,调用father.call(this,“argus”);
问题:子类失去了复用性,每一个都需要独自创建。
借用构造函数的方式很少单独使用。
3.3 组合继承
也叫伪经典继承
结合原型链和借用构造函数,融合了它们的优点,最常用。
思路
使用原型链继承原型,使用构造函数继承实例的属性。
也就是把要共享的部分,加到原型链中。而私有的部分,则加到实例中。
属性是通过实例屏蔽原型的方法来实现私有的,也就是说,同名属性既存在于实例中,又存在于原型中。
function father(name){
this.name=name;
}
father.prototype.sayName=function(){
alert(this.name);
}
function son(name,age){
father.call(this,name);
this.age=age;
}
son.prototype=new father();
3.4 原型式继承
思想
借助已有的对象创建新对象,也就是少掉了new一个父亲的过程。
称为原型式继承,是因为将儿子的原型直接设有已有的对象以作父亲。
function object(father){
function son(){};
son.prototype=father;
return new son();
}
- Object.create()方法
该方法规范了原型式继承。接受两个参数:一个用作新对象原型,(可选)一个为新对象定义额外属性的对象。
3.5 寄生式继承
思路
创建一个仅用于封装继承过程的函数。
function createSon(father){
var son=Object.create(father);
son.sayHi=function(){
alert("hello world!");
}
return son;
}
3.6 寄生组合式继承
寄生组合式继承式引用类型最理想的继承范式。
用于解决组合继承中最大的问题:无论在什么情况下,总会调用超过两次的父亲构造函数。
思路
通过借用构造函数继承属性,通过原型链的混成形式来继承方法。
实现
function inherit(son,father){
var prototype=Object.create(father.prototype);
prototype.constructor=son;
son.prototype=prototype;
}
ch7 函数表达式
函数声明
function sayName(name){
alert(name);
}
函数表达式
var sayName=function(name){
alert(name);
}
函数声明会提升,而函数表达式不会。
1 递归
arguments.callee指向调用的函数。于是可以使用匿名的函数声明来执行递归。
2 闭包
概念
闭包:“怀孕的妈妈”,是指有权访问另一个函数作用域中的变量的函数。
销毁时间
闭包函数在执行完毕之后,其活动对象也不会销毁,因为其中的匿名函数的作用域链仍在引用这个活动对象。等到匿名函数被销毁后,闭包的活动对象才会被销毁。
闭包与变量
闭包只能取得包含函数中任何变量的最后一个值。
解决方法,创建另一个匿名函数。
function createFunc(){
var res= new Array();
for(var i=0;i<10;i++){
res[i]=function(num){
return function(){
return num;
}
}
}
return res;
}
关于this对象
匿名函数的执行环境具有全局性,因此其this对象通常指向window。
让匿名函数访问外部函数中的变量
方法:在外部创建一个新的变量在指向当前对象。
var name="the window";
var object={
name:"my object";
getFunc:function(){
var that=this;
return function(){
return that.name;
}
}
}
alert(object.getFunc()()); // "my object"
*内存泄漏
3 模仿块级作用域
没有块级作用域(ES5中),意味着块语句中的变量,实际上是在包含函数中创建的,而不是在语句中创建的。
匿名函数可以用于模仿块级作用域。
4 私有变量
任何函数中定义的变量,都可以认为是私有变量。
特权方法
有权访问私有变量和私有函数的公有方法。
构造函数模式
funciton MyObject(){
var priVar=2020;
function priFuc(){
alert("hello");
}
this.publicMethod=function(){
priVar++;
return priFunc();
}
}
使用的时候需要new一个MyObject,每一个新对象都拥有自己的变量和方法。
缺点就是每个实例都需要创建同样的一组方法。
原型模式
生成静态私有变量
(funciton(){
var priVar=2020;
function priFuc(){
alert("hello");
}
MyObject=function(){};
Myobject.prototype.publicMethod(){
priVar++;
return priFunc();
}
})();
初始化未经声明的变量,总是会创建一个全局变量,所以能在外部访问到。
创建静态私有变量会因为使用原型而增进代码复用,但每个实例都没有自己的私有变量。
模块模式(module pattern)
单例,是指只有一个实例的对象。
使用一个匿名函数,返回值为一个字面量对象,对象中只包含可以公开的方法和属性。
使用的情况:如果必须创建一个对象,并以某些数据对其进行初始化,同时还要公开一些能够访问这些私有数据的方法,那就可以使用模块模式。
为单例添加私有变量和特权方法:
var singleton = function(){
var priVar=2020;
function priFunc(){
alert("hello");
}
return {
publicPro:true;
publicMethod:function(){
priVar++;
return priFunc();
}
}
}();
增强的模块模式
适用于单例必须是某种类型的实例的。
实现:
将对象字面量编程var obj=new xxx; 添加属性和方法;return obj;即可。