Fork me on GitHub

红宝书的笔记 - 数据类型、设计模式、继承

变量

变量对象(缩写为VO)

一个与执行上下文相关的特殊对象,它存储着在上下文中声明的以下内容:

  • 变量 (var, 变量声明);
  • 函数声明 (FunctionDeclaration, 缩写为FD);
  • 函数的形参
1
2
3
4
5
6
7
var x=30;
function bar(){}
(function baz(){});

//全局上下文中的变量对象包含:
x //30
bar //<funciton>

数据类型

undefined

使用var声明变量但未初始化:

1
2
3
4
5
6
7
var a;
var b = undefined;

console.log(a); //'undefined'
console.log(typeof a == 'undefined'); //true
console.log(typeof b == 'undefined'); //true
console.log(c); //error

null

逻辑上来看相当于空对象指针。

1
2
3
var a = null;
cosole.log(typeof a);//object
console.log(null == undefined); //true

boolean

该类型只有两个字面值:true/false。

可以通过转型函数Boolean()将其他类型的值转换为boolean类型。其中转为false的值如下:

  • String : ‘’(空字符串)
  • Number : 0和NaN
  • Object : null
  • Undefined : undefined

Number

  1. 数值范围

在ECMAScript中超出了最小值Number.MIN_VALUE或者最大值Number.MAX_VALUE会被转换为Infinity(正无穷),如果是负数就是-Infinity(负无穷)。

可以通过isFinite()判断数值是否有穷。
2. NaN

非数值,表示本来要返回数值的操作数未返回数值的情况。NaN进行任何操作结果都是NaN;NaN和任何值都不相等,包括NaN本身。使用isNaN判断是否是非数值。

1
2
3
4
5
//测试isNaN()
isNaN(NaN);//true
isNaN('10');//false
isNaN('blue');//true,不能被转化为number
isNaN(true);//false

  1. 数值转化
    非数值转换为数值:
  • Number()

    1
    2
    3
    4
    Number('blue'); //NaN
    Number(''); //0
    Number('00011'); //11
    Number(true); // 1
  • parseInt()

    1
    2
    3
    4
    5
    6
    7
    8
    parseInt('1234blue'); //1234
    parseInt(''); //NaN
    parseInt('0xA'); //十六进制的10
    parseInt(22.5); //22
    parseInt('070'); //八进制的56
    parseInt('0xAF',16); //175(十六进制)
    parseInt('AF',16); //175(十六进制)
    parseInt('AF'); //NaN
  • parseFloat()

    1
    2
    3
    4
    parseFloat('1234blue'); //1234
    parseFloat(22.34.5); //22.34
    parseFloat(3.125e7); //31250000
    parseFloat('0xA'); //0

String

转化为字符串:

  • toString()

    ps: null和undefined没有这个方法。
    默认不传参以十进制输出。也可以传参输出二进制,八进制,十六进制等。

  • 转型函数String()

    能够将任何类型的值转化为字符串。

Object

数据和功能的集合。Object的每个实例都具有以下属性和方法:

  • constructor : 保存着用于创建当前对象的函数
  • hasOwnProperty(pro) : 检测属性是否存在, ({a:1}).hasOwnProperty('a')
  • isPrototypeOf(obj) : 检测传入的对象是否是当前对象的原型, Object.prototype.isPrototypeOf({})
  • propertyIsEnumerable(pro) : 检测属性是否可以用for-in枚举
  • toLocaleString() : 返回对象的字符串表示
  • toString() : 返回对象的字符串表示
  • valueOf() : 返回对象的字符串、数值、布尔值表示

变量类型

  1. 基本类型值指的是简单的数据,按值访问,可以操作保存在变量中的实际值
  2. 引用类型值指的是保存在内存中的对象,按引用访问

复制引用类型值时,除了会复制变量中的值,而且两个变量引用的是同一对象,改变其中一个变量也会影响另一个变量。

数组方法

引用类型的值是引用类型的实例。

栈方法

  1. push():接收任意数量的参数,并逐个添加到数组末尾,并返回修改后的数组长度
  2. pop():从数组末尾移除最后一项,减少数组长度并返回移除的项

队列方法

  1. shift():移除数组第一项并返回该项,数组长度减一
  2. unshift():在数组前端添加任意个数并返回新数组长度

归并方法

  1. reduce():从数组的第一项开始,逐个遍历到最后
  2. reduceRight():从数组的最后一项开始,向前遍历到第一项

接收一个参数:在每一项调用的函数和作为归并基础的初始值

1
2
3
4
5
6
var arr = [1,2,3,4];
//传入参数:前一个值,当前值,索引,数组对象
var sum = arr.reduce(function(prev,cur,index,array){
return prev + cur;
});
console.log(sum);//10

Date类型

创建一个日期对象:

1
var now = new Date();

不传递参数时,新建对象自动获得当前时间日期。

  • Date.parse(date):接收一个表日期的字符串参数,返回相应毫秒数或者NaN。

    1
    2
    3
    var date1 = new Date(Date.parse("May 25,2004"));
    //等价于
    var date1 = new Date("May 25,2004");
  • Date.UTC():返回毫秒数,参数是(年份,基于0的月份,日期,小时,分钟,秒),年份和月份是必须的。

    1
    2
    3
    4
    5
    6
    7
    //GMT时间2000-1-1 00:00:00
    var date2 = new Date(Date.UTC(2000,0));

    //GMT时间2001-2-5 17:55:55
    var date2 = new Date(Date.UTC(2001,1,5,17,55,55));
    //等价于
    var date2 = new Date(2001,1,5,17,55,55);
  • Date.now():返回调用这个方法的时间毫秒数。

    1
    2
    3
    var start = Date.now();
    //等价于
    var start = +new Date();

日期格式化方法

将日期格式化为字符串的方法:

  • toDateString():显示日期
  • toTimeString(): 显示时间
  • toLocaleDateString(): 显示地区日期
  • toLocaleTimeString(): 显示地区时间
  • toUTCString(): 显示UTC日期

RegExp

正则式的元字符:( [ { \ ^ $ | ) ? * + . ] }

1
2
3
4
5
6
7
8
9
10
11
var reg = /.at/g; //返回以at结尾的字符串
var re = null,i;
for(i = 0; i < 10, i++){
re = /cat/g;
re.test("catastrophe");
}//返回false

for(i = 0; i < 10, i++){
re = new RegExp("cat","g");
re.test("catastrophe");
}//返回true
  • . : 匹配除换行符之外的所有字符
  • \d : 匹配数字,相当于[0-9]
  • \w : 字母或数字或下划线或汉字等
  • * : 表示前边的内容可以连续重复使用任意次数(包括0次)
  • \s : 匹配任意空格符,包括换行符和tab
  • \b : 表示单词的开头或结尾,也就是单词的分界处
  • + :表示出现至少一次或多次
  • \r :回车
  • \t :制表符,tab
  • ^ :匹配字符串的开始
  • $ :匹配字符串结束
  • ? :表示出现0次或一次
  • {n} :重复n次
  • {n,} :重复n次或更多次
  • {n,m} :重复n到m次
  • \W :匹配任意不是字母,数字,下划线,汉字的字符
  • \S :匹配任意不是空白符的字符
  • \D :匹配任意非数字的字符
  • \B : 匹配不是单词开头或结束的位置
  • [^xy] :匹配除了xy以外的任意字符
  • [\s\S] :匹配任意字符,[^] 与 [\s\S] 等价
  • (?=exp) :零宽度正预测先行断言,断言自身出现的位置的后面能匹配表达式exp
  • (?!exp) :零宽度负预测先行断言,断言此位置的后面不能匹配表达式exp
  • (?#comment) :注释
  • .* :贪婪匹配,匹配尽可能多的字符
  • *? :懒惰匹配,也就是匹配尽可能少的字符,重复任意次
  • +? :重复1次或更多次,但尽可能少重复
  • ?? :重复0次或1次,但尽可能少重复
  • {n,m}? :重复n到m次,但尽可能少重复
    1
    var reg = \<(\w+)>\g; // 查找尖括号括起来的字母或数字(即HTML/XML标签)

toLocaleString()和toString()都发挥正则表达式的字面量,与创建表达式的方式无关。

1
2
var reg = new RegExp("\\[bc\\]at","gi");
reg.toString(); // /\[bc\]at/gi

实例方法

  • exec(str): 返回包含第一个匹配性信息的数组或null。在数组中,第一项是与整个模式匹配的字符串,其他项是与模式中的补货组匹配的字符串。返回的数组有两个额外的属性:
    index:匹配项在字符串的位置;
    input:应用正则的字符串;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    var text = "mon and dad and baby";
    var pattern = /mon( and dad( and baby)?)?/gi;

    var matches = pattern.exec(text);
    console.log(matches.index); //0
    console.log(matches.input); //"mon and dad and baby"
    console.log(matches[0]); //"mon and dad and baby"
    console.log(matches[1]); //"and dad and baby"
    console.log(matches[2]); //"and baby"
  • test(str): 测试是否匹配

  • compile(): 对正则表达式进行编译,被编译过的正则在使用的时候效率会更高,适合于对一个正则多次调用的情况

  • str.match(reg): 输出结果在不是全局匹配的情况下和exec方法的结果一致即一个数组并带有额外的属性,如果采用全局匹配,则不返回任何和其被匹配字符串相关的信息,只返回匹配的结果。

Function

函数是对象。定义函数:函数声明和函数表达式。

函数的内部属性

  • arguments: 保存函数参数,类数组对象。属性callee: 指针,指向拥有这个arguments的函数。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    //阶乘函数
    function factorial(num){
    if(num < 2){
    return 1;
    }else{
    return num * factorial(num-1);
    }
    }
    //等价于(严格模式下不支持)
    function factorial(num){
    if(num < 2){
    return 1;
    }else{
    return num * arguments.callee(num-1);
    }
    }
  • this: 引用的是函数执行的环境对象,当全局调用时,指向的是window

    1
    2
    3
    4
    5
    6
    7
    8
    9
    window.color = 'red';
    var o = {color:'blue'};
    function sayColor(){
    console.log(this.color);
    }

    sayColor(); //red
    o.sayColor = sayColor;
    o.sayColor(); //blue
  • caller(ES5): 保存着调用当前函数的函数的引用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    function outer(){
    inner();
    }
    funtion inner(){
    console.log(inner.caller);
    //指向outer()
    //console.log(arguments.callee.caller);
    }
    outer();

函数的属性和方法

属性:

  • length: 表示函数希望接收的参数个数
  • prototype:保存所有的实例方法

方法:
以下两个方法都是在特定的作用域调用函数,实际上等于设置函数体内this对象的值。

  • apply()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    //作用1:传递参数
    function sum(a,b){
    return a + b;
    }
    function callSum(a,b){
    return sum.apply(this,arguments);//this指向window
    //相当于
    // return sum.apply(this,[a,b]);
    }
    callSum(10,20); //30
  • call()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    //作用1:传递参数
    function sum(a,b){
    return a + b;
    }
    function callSum(a,b){
    return sum.call(this,a,b);
    }
    callSum(10,20); //30
    //作用2:扩充函数运行的作用域
    window.color = 'red';
    var o = {color:'blue'};
    function sayColor(){
    console.log(this.color);
    }

    sayColor(); //red
    sayColor.call(this); //red
    sayColor.call(window); //red
    sayColor.call(o); //blue
  • bind(): 创建一个函数的实例,this值被绑定到传给bind()函数的值。

    1
    2
    3
    4
    5
    6
    7
    8
    window.color = 'red';
    var o = {color:'blue'};
    function sayColor(){
    console.log(this.color);
    }

    var sayColorFun = sayColor.bind(o);
    sayColorFun(); //blue

基本包装类型

用于操作基本类型值的特殊的引用类型:Boolean,Nymber,String

引用类型和基本包装类型的主要区别就是对象的生存周期。使用new创建的引用类型实例在执行流离开作用域之前一直保存在内存中。自动创建的基本包装类型的对象值存在于一行代码执行的瞬间,然后立即销毁。

对基本包装类型的实例调用typeof返回object,并且转化为布尔类型时都是true。

1
2
3
//根据传入的值的类型返回相应的基本包装类型实例
var obj = new Object('text');
alert(obj instanceof String); //true

使用new调用基本包装类型的构造函数和直接调用转型函数不一样。

1
2
3
4
5
6
var value = "25";
var num = Number(value);//转型函数
console.log(typeof num); // number

var obj = new Number(value);//构造函数
console.log(typeof obj); // object

Boolean类型

Number类型

  • toFixed(): 按指定的小数位返回数值的字符串表示。
  • toExponential(): 以指数表示法当数值的字符串形式。参数指定输出结果中的小数位数。
  • toPrecision(): 返回最合适的数值格式
    1
    2
    3
    4
    var num = 99;
    num.toPrecision(1); //"1e+2"
    num.toPrecision(2); //"99"
    num.toPrecision(3); //"99.0"

不建议实例化:

1
2
3
4
5
6
7
var numobj = new Number(10);
var numval = 10;
console.log(typeof numobj); //object
console.log(typeof numval); //number

console.log(numobj instanceof Number); //true
console.log(numval instanceof Number); //false

String类型

单体内置对象

Global对象

全局对象

Math对象

  1. min()和max()

    1
    2
    3
    var max = Math.max(1,55,22);  //55
    var arr = [1,3,5,7];
    var min = Math.apply(Math,arr); //1
  2. 舍入方法

  • ceil(num):向上取整
  • floor(num):向下取整
  • round(num):四舍五入
  1. 其他方法
  • random():返回0到1之间的随机数
  • abs(num):绝对值
  • exp(num):返回Math.E(自然对数的底数,常量e)的num次幂
  • log(num):自然对数
  • pow(num,power):返回num的power次幂
  • sqrt(num):平方根
  • acos(num):反余弦值

属性类型

数据属性:

要修改属性默认特性需使用Object.defineProperty()。接收三个参数,属性所在对象、属性名、描述符对象。

描述符对象的属性:configurable(能否delete或修改属性特性)、enumerable(for-in能否返回属性)、writable(能否修改属性值)、value(属性的数据值)

1
2
3
4
5
6
7
var person = {};
Object.defineProperty(person,"name",{
writable:false,
value:'Jack'
});
person.name = 'Rose';
console.log(person.name); //Jack

PS:configurable设置为false表示不可配置之后,就不能再变回可配置了,此时修改除writable之外的特性都会报错。

访问器属性

包含getter()和setter()。访问器属性必须用Object.defineProperty()定义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var book = {_year:2004,edition:1};
Object.defineProperty(book,'year',{
get:function(){
return this._year;
},
set:function(newValue){
if(newValue > 2004){
this._year = newValue;
this.edition += newValue -2004;
}
}
})
book.year = 2005;
alert(book.edition); //2

定义多个属性和读取属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var book = {};
Object.defineProperties(book,{
_year: {
writable:true,
value:2004
},
year:{
get:function(){
return this._year;
},
set:function(newValue){
if(newValue > 2004){
this._year = newValue;
}
}
}
})
var des = Object.getOwnPropertyDescriptor(book,'_year');
alert(des.value); //2004

创建对象

为了解决构造函数和字面量创建对象的缺点:创建多个对象时代码重复

工厂模式

用函数来封装以特定接口创建对象的细节。缺点:无法识别对象

1
2
3
4
5
6
7
8
9
10
11
function createPerson(name,age){
var o = new Object();
o.name = name;
o.age = age;
o.sayName = function(){
console.log(this.name);
}
return o;
}
var person1 = createPerson("A",20);
var person2 = createPerson("B",28);

构造函数模式

与工厂模式的不同:没有显示的创建对象;直接将属性和方法赋予了this;没有return;函数名首字母大写;创建实例必须使用new操作符(构造函数与普通函数的调用方式不同);

1
2
3
4
5
6
7
8
9
10
11
12
function Person(name,age){
this.name = name;
this.age = age;
this.sayName = function(){
console.log(this.name);
}
}
var person1 = new Person('A',20);
var person2 = new Person('B',28);
console.log(person1 instanceof Object); //true
console.log(person1 instanceof Person); //true
//可以将构造函数的实例标识为一种特定的类型(识别对象)

缺点:每个方法都要在实例上重新创建一遍(不同实例的同名方法是不同的)

1
2
3
4
5
6
7
8
9
10
11
console.log(person1.sayName == person2.sayName); //false
//优化:解决了两个函数调用同一方法的问题
function Person(name,age){
this.name = name;
this.age = age;
this.sayName = sayName
}
function sayName(){
console.log(this.name);
}
//缺点:可能需要定义多个全局函数

原型模式

prototype属性是一个指针,指向原型对象,通过调用构造函数创建的对象实例的原型对象。原型对象的优点:所有的实例都共享了包含的属性和方法。将对象实例的信息都添加到原型对象中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
function Person(){
}
Person.prototype.name = "A";
Person.prototype.age = 20;
Person.prototype.sayName = function(){
console.log(this.name);
};
var person1 = new Person();
var person2 = new Person();

person2.name = "B";
alert(person1.name);//"A"--来自原型,说明不能通过实例重写原型中的值
alert(person2.name);//"B"--来自实例,屏蔽原型对象中同名的属性,切断了和原型的联系

//hasOwnProperty检测属性是否在实例中
console.log( person1.hasOwnProperty("name")); //false
console.log( person2.hasOwnProperty("name")); //true

//in 判断对象是否能访问该属性
console.log("name" in person1); //true
console.log("name" in person2); //true

//Object.keys()
console.log(Object.keys(Person.prototype)); //['name','age','sayName']
console.log(Object.keys(person2)); //['name']

console.log(Object.getOwnPropertyNames(Person.prototype));
//['constructor','name','age','sayName']

person2.name = null;
alert(person2.name);//""

delete person2.name; //删除实例属性
alert(person2.name);//"A" --来自原型
console.log( person2.hasOwnProperty("name")); //false
//Person.prototype指向原型,Person.prototype.constructor指向Person

for-in循环时返回的是所有对象能够访问的属性(包括原型中的属性)。如果要获取所有可枚举属性,可以使用Object.keys(),返回数组。如果要获取所有实例属性,不论是否可枚举,使用Object.getOwnPropertyNames()

缺点:原型的共享性会导致实例的引用类型值相同

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function Person(){
}
Person.prototype = {
//字面量的方式重写原型对象,会切断现有实例和新原型的联系
constructor:Person,//指向Person
name:'A',
age:20,
sayName : function(){
console.log(this.name);
},
friends:['b','c']
}
var person1 = new Person();
var person2 = new Person();

person1.friends.push('d');

console.log(person1.friends); //b,c,d
console.log(person2.friends); //b,c,d
console.log(person1.friends == person2.friends); //true

组合使用构造函数模式和原型模式

构造模式用于实例属性,原型模式用于定义方法和共享的属性。可以用来定义引用类型的一种默认模式。(推荐)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function Person(name,age){
this.name = name;
this.age = age;
this.friends = ["a","b"];
}
Person.prototype = {
constructor:Person,
sayName:function(){
alert(this.name);
}
}
var person1 = new Person("A",20);
var person2 = new Person("B",28);
person1.friends.push("c");
alert(person1.friends === person2.friends);//false
alert(person1.sayName === person2.sayName);//true

动态原型模式

信息封装在构造函数中,通过构造函数初始化原型。不能使用字面量重写原型。

1
2
3
4
5
6
7
8
9
10
11
function Person(name,age){
this.name = name;
this.age = age;
if(typeof this.sayName != "function"){
Person.prototype.sayName = function(){
alert(this.name);
}
}
}
var person3 = new Person("C",30);
person3.sayName();

寄生构造函数模式

这个模式可以在特殊情况下用来为对象创建构造函数。不能依赖instanceof确定对象类型。

1
2
3
4
5
6
7
8
9
10
11
function Person(name,age){//封装创建对象并返回新对象
var o = new Object();
o.name = name;
o.age = age;
o.sayName = function(){
console.log(this.name);
}
return o;
}
var person1 = new Person("A",20);
person1.sayName();

稳妥构造函数模式

和寄生构造函数类似,不同点:新创建对象的实例方法不引用this,不使用new操作符调用构造函数。(在安全的环境中使用)

1
2
3
4
5
6
7
8
9
10
11
function Person(name,age){
var o = new Object();
o.name = name;
o.age = age;
o.sayName = function(){
console.log(name);
}
return o;
}
var person1 = Person("A",20);
person1.sayName();

继承

原型链

原型链是实现继承的主要方法。在继承时不能使用字面量创建原型。原型链的基本模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function Super(){
this.property = true;
}
Super.prototype.getSuper = function(){
return this.property;
}
function Sub(){
this.subproperty = false;
}
//继承Super
//让原型对象等于另一个类型的实例
Sub.prototype = new Super();
Sub.prototype.getSub = function(){
return this.subproperty;
}

var instance = new Sub();
console.log(instance.getSuper()); //true

缺点:引用类型值会被所有实例共享;在创建子类型时不能给超类型的构造函数传递参数。

借用构造函数

在子类型构造函数内部调用超类型构造函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
function Super(){
this.nums = [1,2,3];
}
function Sub(){
//继承
Super.call(this);
}
var instance1 = new Sub();
instance1.nums.push(4);
console.log(instance1.nums); //[1,2,3,4]

var instance2 = new Sub();
console.log(instance2.nums); //[1,2,3]

子类型构造函数像超类型构造函数传递参数:

1
2
3
4
5
6
7
8
9
10
11
function Super(name){
this.name = name;
}
function Sub(){
//继承
Super.call(this,'A');
this.age = 20;
}
var instance = new Sub();
console.log(instance.name,instance.age);
//'A',20

缺点:不支持函数复用;超类型的原型中定义的方法对子类型不可见;

组合继承

使用原型链实现对原型属性和方法的继承,使用借用构造函数实现对实例属性的继承。(推荐)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
function Super(name){
this.name = name;
this.nums = [1,2,3];
}
Super.prototype.sayName = function(){
console.log(this.name);
}
function Sub(name,age){
//继承
Super.call(this,name);//第一次调用Super
this.age = age;
}
Sub.prototype = new Super();//第二次调用Super
Sub.prototype.constructor = Sub;
Sub.prototype.sayAge = function(){
console.log(this.age);
}

var instance1 = new Sub('A',20);
instance1.nums.push(4);
console.log(instance1.nums); //[1,2,3,4]
instance1.sayName(); //A
instance1.sayAge(); //20

var instance2 = new Sub('B',30);
console.log(instance2.nums); //[1,2,3]
instance2.sayName(); //B
instance2.sayAge(); //30

缺点:无论怎样都会调用两次超类型构造函数

原型式继承

借助原型基于已有对象创建新对象。可以用方法Object.create()实现。参数:用作新对象原型的对象、为新对象定义额外属性的对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var person = {
name :'A',
nums:[1,2,3]
}
var person1 = Object.create(person);
person1.name = 'person1';
person1.nums.push(4);

var person2 = Object.create(person);
person2.name = 'person2';
person2.nums.push(5);
console.log(person.nums);//[1,2,3,4,5]

var person3 = Object.create(person,{
name:{
value:'person3'
}
});
console.log(person3.name);//person3

寄生式继承

和寄生构造函数类似。

寄生组合式继承

通过借用构造函数继承属性,通过原型链的混合形式来继承方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function inheritPrototype(sub,super){
var prototype = object(super.prototype); //创建对象
prototype.constructor = sub;
sub.prototype = prototype;
}
function Super(name){
this.name = name;
this.color = ['red','blue','green'];
}
Super.prototype.sayName = function(){
console.log(this.name);
}
function Sub(name,age){
Super.call(this,name);
this.age = age;
}
inheritPrototype(Sub,Super);
Sub.prototype.sayAge = function(){
console.log(this.age);
}

函数表达式

函数声明,函数表达式。函数表达式创建的函数是匿名函数。

递归

闭包

有权访问另一个函数作用域中的变量的函数。

作用域链是一个指向变量对象的指针列表,只引用但不包含变量对象。闭包保存的是包含函数的整个活动变量,所以闭包只能取得包含函数中任何变量是最后一个值。

闭包中的this对象:this对象指向函数的执行环境。匿名对象的执行环境具有全局性,所以this对象指向window。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var name = 'window';
var object ={
name:'object',
getNameFunc:function(){
return function(){//闭包
return this.name;
}
},
sayName:function(){
return this.name;
}
}
console.log(object.getNameFunc()); //'window'
console.log(object.sayName()); //'object'

把外部作用域的this保存在闭包能访问到的变量中:

1
2
3
4
5
6
7
8
9
10
11
var name = 'window';
var object ={
name:'object',
getNameFunc:function(){
var _this = this;
return function(){
return _this.name;
}
}
}
console.log(object.getNameFunc()); //'object'

模仿块级作用域和私有变量

1
2
3
(function(){
//私有作用域
})();

任何在函数内部定义的变量都是私有变量。特权方法:有权访问私有变量和私有函数的方法。
创建特权方法:

  1. 在构造函数中定义
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    function MyObj(){
    var val = 0;
    function privateFun(){
    return false;
    }

    //特权方法(闭包)
    this.publicFun = function(){
    val++;
    return privateFun();
    }
    }
    var obj = new MyObj();
    console.log(obj.publicFun());

缺点:每个实例都会创建同样的方法
2. 静态私有变量:在私有作用域中定义私有变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
(function(){
//私有变量
var privateval = 0;
function privateFun(){
return false;
}
//构造函数
MyObj = function(){
//初始化未经声明的变量会创建一个全局变量,可以在私有作用域外被访问到(严格模式下报错)
}
//特权方法
MyObj.prototype.publicFun = function(){
privateval++;
return privateFun();
}
})()

缺点:每个实例都共享了方法,但没有自己的私有变量
3. 模块模式:为单例创建私有变量和特权方法,单例是只有一个实例的对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//创建一个对象并对其初始化,并公开一些访问私有数据的方法
var single - function(){
var privateval = 0;
function privateFun(){
return false;
}
//特权方法
return {
publicproperty:true,
publicFun:function(){
privateval++;
return privateFun();
}
}
}

浏览器对象模型(BOM)

BOM的核心是window。

全局对象和window对象

全局变量是window对象的属性。全局变量不能通过delete删除,但直接在window上定义的属性可以删除。

1
2
3
4
5
6
7
8
9
10
11
var age = 10;
window.color = 'red';

delete window.age; //false
delete window.color; //true

console.log(window.age); //10
console.log(window.color); //undefined

console.log(oldvalue);//报错
console.log(windwo.oldvalue); //undefined

location对象

保存当前窗口的文档信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//解析查询url
function getQueryArgs(){
//取得查询字符串并去除开头问号
var qs = (location.search.length > 0 ? location.search.substring(1) : ''),
args = {},
items = qs.length ? qs.split("&"):[],
item = null,
name = null,
value = null;
for(var i = 0,len = items.length;i < len; i++){
item = items[i].split('=');
name = decodeURLComponent(item[0]);//解码
value = decodeURLComponent(item[1]);

if(name.length){
args[name] = valuel
}
}
return args;
}

使用location对象可以改变浏览器的位置。

1
2
3
4
5
6
7
8
9
10
location.assign(url);
//相当于
window.location = url;
location.href = url;

location.replace(url);
//不在历史记录中生成新纪录,不能回退

location.reload(); //重新加载页面(可能从缓存中加载)
location.reload(true); //从服务器重新加载页面

保存浏览器信息。

检测插件

1
2
3
4
5
6
7
8
9
10
11
12
13
//在ie中无效
function hasPlugin(name){
name = name.toLowerCase();
var plugins = navigator.plugins,
len = plugins.length;
for(var i = 0; i < len; i++){
if(plugins[i].name.toLowerCase().indexOf(name) > -1){
return true;
}
}
return false;
}
console.log(hasPlugin("Flash"));

其他对象

  • screen对象:显示器信息
  • history对象:历史记录
    1
    2
    3
    4
    5
    6
    7
    8
    9
    history.go(-1); //后退一页
    history.back();

    history.go(1); //前进一页
    history.forward();

    history.go(2); //前进两页

    history.go("baidu.com"); //跳到最近的baidu.com

客户端检测

能力检测

检测浏览器是否支持某功能。

1
var hasNSPlugins = !!( navigator.plugins&& navigator.plugins.length);

怪癖检测

针对浏览器的特殊行为进行检测。

用户代理检测

文档对象模型(DOM)

针对XML扩展后用于HTML的应用程序编程接口(API)。DOM把页面映射为一个多层节点结构,或者说它将网页中的HTML文档抽象为内存中的节点对象树(DOM Tree)。树中的每一个节点对象对应HTML文档中的一个元素。

Node类型

有12种节点类型。

1
2
3
4
5
6
7
8
if(someNode.nodeType == 1){//元素节点
console.log("element");
var name = someNode.nodeName; //元素的标签名
var value = someNode.nodeValue; //null
}
if(someNode.nodeType == 3){//文本节点
console.log("text");
}
  1. 节点关系

每个节点都有childNodes属性,保存着NodeList对象(类数组对象,保存一组有序的节点)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var firstChild = someNode.childNodes[0];
var secondChild = someNode.childNodes.item(1);
var len = someNode.childNodes.length;

var arr = Array.prototype.slice.call(someNode.childNodes,0);//转化为数组
//更完善的方法
function convertToArr(nodes){
var array = null;
try{
array = Array.prototype.slice.call(nodes,0);
}catch{
array = new Array();
for(var i= 0,len = nodes.length;i<len;i++){
array.push(nodes[i]);
}
}
return array;
}

var parent = someNode.parentNode;
var next = someNode.nextSibling;
var prev = someNode.previousSibling;

console.log(hasChildNodes(someNode)); //是否有子节点

  1. 操作节点

appendChild():向childNodes末尾添加节点,返回新增节点。如果节点已经存在,就把它移动到末尾。

1
2
var node = someNode.appendChild(someNode.firstChild);
console.log(node == someNode.firstChild); // false

insertBefore():向指定位置插入节点,返回新增节点。

1
2
3
4
5
6
7
8
//插入最后一个子节点
var node = someNode.insertBefore(newNode,null);

//插入第一个子节点
var node = someNode.insertBefore(newNode,someNode.firstChild);

//插入最后一个子节点之前
var node = someNode.insertBefore(newNode,someNode.lastChild);

replaceChild():替换节点。

1
2
//替换第一个子节点
var node = someNode.replaceChild(newNode,someNode.firstChild);

removeChild():移除节点。

1
2
//移除第一个子节点
var node = someNode.removeChild(someNode.firstChild);

cloneNode():复制节点,接收一个布尔参数,表示是否深复制。深复制:复制节点及其子节点;浅复制:仅复制节点。
normalize():处理文本节点(删除空文本节点,合并相邻文本节点)

document类型

表示文档,表示html页面。nodeType值为9,nodeName为#document。

1
2
3
4
5
6
7
8
9
10
11
var html = document.documentElement;
console.log(html === document.firstChild); //true

var body = document.body;
var title = document.title;

var doctype = document.doctype; //有浏览器兼容性问题

var url = document.URL; //完整的url
var dimain = document.dimain; //域名(可设置)
var referrer = document.referrer; //来源页面的url
  1. 查找元素

getElementById():返回第一次匹配的元素。在ie7中可能返回name特性的元素。
getElementByTagName():根据标签名查找元素,返回包含零或多个元素的NodeList。

1
2
3
4
5
6
7
8
9
10
11
12
var imgs = document.getElementByTagName("img");
console.log(imgs.length);
console.log(imgs[0].src);
//相当于
console.log(imgs.item(0).src);

//namedItem: 通过name特性取得元素
var myimg = imgs.namedItem("myimg");
//相当于
var myimg = imgs["myimg"];

var all = document.getElementByTagName("*");//所有标签

getElementByName():根据name特性返回所有元素
2. 特殊集合

document.anchors:所有带name特性的<a>元素。

document.forms:所有<form>。

document.images:所有<img>。

document.links:所有带href特性的<a>元素。
3. DOM一致性检测

1
2
var hasXmlDom = document.implementation.hasFeature("XML","1.0");
//参数:检测的DOM功能名称和版本号
  1. 文档写入

document.write():写入s输出流。

document.writeln():写入时加换行符。

document.open():打开输出流。

document.close():关闭输出流。

Element类型

nodeType值为1。可以用nodeName或者tagName获取元素标签名,在html中标签名以大写返回。

  1. HTML元素

特性:id,title,lang(语言),dir(方向),className
2. 操作特性

特性的名称不区分大小写。
getAttribute(attrname):获取某特性,返回字符串

setAttribute(attrname,attrvalue):设置特性

removeAttribute(attrname):移除特性
3. attributes属性

Element类型有attributes属性。包含元素所有的属性Attr节点,可以用nodeName获取特性的名称,nodeValue获取特性值。一般用于遍历元素的特性。

1
2
3
4
5
6
7
var id = element.attributes.getNamedItem("id").nodeValue;
//相当于
var id = element.attributes["id"].nodeValue;
//setNamedItem():添加特性;removeNamedItem():移除特性;item(index):查找特性
if(element.attributes["id"].specified){
//是否指定的该特性
}

  1. 创建元素
    1
    var div = document.createElement('div');
-------------完结撒花 -------------

本文标题:红宝书的笔记 - 数据类型、设计模式、继承

文章作者:咕噜咕噜

发布时间:2019年01月21日

最后更新:2022年09月22日

原始链接:https://aartemida.github.io/2019/01/21/红宝书的笔记1/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。