JavaScript高级程序设计复习笔记(二)

Array类型的数组数量问题

1
2
var ar = [,,,];
console.log(arr.length); //3(最后一个逗号后面不计数)

小技巧,利用length在数组的末尾添加新项

1
2
var colors = ["red","blue"];
colors[color.length] = "black";

join()方法用于给数组提供分隔符以构建字符串

1
2
3
var colors = ["red","green"];
console.log(colors.join("||"));
//输出 red||green,当直接调用join()时,使用默认的“,”分隔。

栈方法(LIFO):后进先出;队列方法(FIFO):先进先出

方法类比:

方法名称 方法作用
push() 从数组后方依次添加
unshift() 从数组前方依次添加
pop() 从数组后方删除最后一个,并返回被删除值
shift() 从数组前删除第一个,并返回第一个被删除的值

重排序方法

1
2
3
var values = [0,1,2,10,15];
values.sort();
console.log(values); //0,1,10,15,2

原因:排序时sort将所有数组值均转化为字符串值再拍讯。
解决方法:新建一个compare()方法进行比较

1
2
3
4
5
6
7
8
9
10
11
12
13
function compare(value1,value2){
if(value1 < value2){
return -1;
} else if (value1 > value2){
return 1;
} else {
return 0;
}

}
var values = [0,1,5,10,15];
values.sort(compare);
console.log(values); //0,1,5,10,15

操作方法

concat():链接两数组,若不传递参数,则只复制一份,不会额外向原数组增加内容。
slice():复制当前数组,参数为开始和结束位置。

1
2
3
4
5
var arr = [1,2,3,4,5];
var arr1 = arr.slice(1);
console.log(arr1); //2,3,4,5
var arr2 = arr.slice(1,4);
console.log(arr2); //2,3,4

注:当slice传参为负时,则要加上数组长度,以得到正确结果。
splice():有删除,插入,替换三个作用
删除:splice(要删除的第一项,删除的项数);
插入:splice(起始位置,0,要插入的项);
替换:splice(起始位置,要删除的项,要替换的项);

迭代方法

every():为数组每一次运行给定函数,每一项为true,则返回true。
fliter():为数组的每一项执行给定函数,返回true项组成的数组。
forEach():每一项执行函数,无返回值。
map():为每一项执行函数,返回每次调用结果组成的数组。
some():为每一项执行函数,任意项返回true,则返回true。

归并方法

reduce():迭代数组所有项,构建一个最终返回值。
reduceRight():迭代数组所有项,从最后一个数开始。

函数声明提升

解析器会在代码执行前进行一个函数声明提升的过程,读取并将函数声明添加到执行环境中。

1
2
3
4
5
6
7
8
console.log(num1(1,2));   //3
console.log(num2(1,2)); //报错
function num1(m , n){
return m + n;
}
num2 = function(m , n){
return m + n;
}

注:要访问函数的指针而不执行函数,则需要在调用时去掉函数的圆括号进行调用。

使用arguments.callee()消除紧密耦合

1
2
3
4
5
6
7
8
//这是一个普通的递归函数
function factioral(num){
if(num <= 1){
return 1;
} else {
return num*arguments.callee(num - 1);
}
}

函数的prototype属性

对于所有属性,prototype属性时保存他们的所有实例方法的真正所在,prototype属性包含两个非继承来的方法:apply()和call()。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function sum(num1,num2){
return num1 + num2;
}
function callSum1(num1,num2){
return sum.apply(this.arguments); //传入arguments对象
}
function callSum2(num1,num2){
return sum.apply(this,[num1,num2]); //传入数组
}
function callSum3(num1,num2){
return sum.call(this,num1,num2);
}
console.log(callSum1(10,10)); //20
console.log(callSum2(10,10)); //20
console.log(callSum3(10,10)); //20

call和apply的作用相同,当包含函数中接受数组时,apply更加合适。
apply和call的最大用处,是可扩充函数赖以执行的作用域7.

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

基本包装类

基本包装类的自用创建的引用类型实例,会在代码执行后立即销毁。

1
2
3
var s1 = "some test";
s1.color = "red";
colsole.log(s1.color); //undefined

注:直接使用转型函数,则返回相应结果。使用构造函数,则返回相应函数的实例。

1
2
3
4
5
var value = "25";
var number = Number(value); //使用转型函数
console.log(typeof number); //number
var obj = new Number(value); //使用构造函数
console.log(typeof obj); //Object

注:new会创造一个object对象,new Boolean(false)可判定为true。

数值类型的转换

.toFixed(n):表示转换为几位小数的值。
.toExponential(n):表示转换为几位指数值
.toPreoision(n):表示转换为n位最合适的数
.charAt():输出当前字符串的第n位字符(从0开始)
.charCodeAt():输出当前字符串的第n位字符的ASCII码
.trim():删除前缀和后缀的空格,并返回结果
.toLowerCase():转换为小写
.toUpperCase():转换为大写
.match():查找字符串相匹配的位置(search())的作用相同
.replace():传递两个参数,用后面的参数替换前面的参数
.localeCompare():比较,返回-1,0,1的其中一种
.fromCharCode():接收字符编码,转换为字符串
.eval():相当于一个独立解析器,在其中创建任何变量或函数都不会被提升

typrof()和instanceof()得到的结果不同,如下:

1
2
3
4
5
6
var numberObject = new Number(10);
var numberValue = 10;
console.log(typeof numberObject); //Object
console.log(typeof numberValue); //number
console.log(numberObject instanceof Number); //true
console.log(numberValue instanceof Number); //false

面向对象

数据属性

configurable:表示能否通过delete删除属性从而重新定义属性,默认为true。
enumerable:表示能否通过for-in循环返回属性,默认为true。
writable:表示能否修改属性值,默认为true。
value:表示包含这个属性的数据值,默认为undefined。
以上的属性,可以通过` Object.defineProperty()方法进行修改。但是一旦属性定义为不可配置,再调用该方法修改除writable以外的属性,都会导致错误。定义多个属性,可用Object.defineProperties()。
Object.getOwnPropertyDescriptor()方法,可取得给定属性的描述符。

访问器属性

访问器属性不包含属性值,但包含一对getter和setter函数,读取时调用getter,写入时调用setter。
注:在某参数前加“_”表示该参数只能通过对象方法访问。

工厂模式

抽象了创建具体对象的过程,用函数来封装以特定接口创建对象的细节。

1
2
3
4
5
6
7
8
9
10
11
function createPerson(name,age,job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
alert(this.name);
}
return o;
}
var person1 = createPerson("changer",18,"Engineer");

函数createPerson()能够根据接受的参数来构建一个包含所有必要信息的person对象,可无数次调用,工厂模式虽然解决了多个相似对象的问题,却没有解决对象识别的问题。

构造函数模式

1
2
3
4
5
6
7
8
9
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function(){
alert(this.name);
}
}
var person1 = Person("changer","18,"Engineer");

构造函数始终都应以一个大写字母开头,非构造函数应以小写字母开头。构造函数的实例都有constructor()属性,该属性指向Person。
构造函数的缺点:每个方法都要在每个实例上重新创建一遍,因此可将函数定义转移到函数外面以解决问题:

1
2
3
4
5
6
7
8
9
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
}
function sayName(){
alert(this.name);
}
var person1 = Person("changer","18,"Engineer");

以上做法解决了两个函数做同一件事的问题,但是在全局作用域中定义的函数实际上只能被某个对象调用,这让全局作用域名不副实,如果要定义多个方法,全部作为全局变量就毫无封装性可言了。

原型模式

注:当原型中有某属性而且实例中也有该属性时,原型中的该属性值就用不到了,也无法被检测出来。

1
2
3
4
5
6
7
8
9
10
11
12
function Person(){}
Person.prototype = {
name : "Nicholas",
age : 29,
job : "Software Engineer",
sayName : function(){
alert(this.name);
}
}
var friend = new Person();
alert(friend instanceof Person); //true
alert(friend.constructor == Person); //false

以上写法完全重写了默认的prototype对象,因此constructor属性也就变成了新对象的constructor属性,指向Object构造函数。
在浏览器内核兼容ES5的情况下,可在下面增加:

1
2
3
4
Object.defineProperty(Person.prototype,"constructor",{
enumerable : false,
value : Person
})

该写法改进了在Person.prototype中设置导致enumerable属性被设置为true的问题。
但是原型对象还存在问题:省略了构造函数传递初始化参数这一操作,会使所有实例在默认情况下取得相同值。

混成模式

组合使用了构造函数模式和原型模式,构造函数模式用于定义实例属性,原型模式用于定义方法和共享的属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.friends = ["aa","bb"];
}
Person.prototype = {
constructor : Person;
sayName : function(){
alert(this.name);
}
}
var person1 = new Person("changer",18,"worker");

继承

通过原型链实现继承时,不能使用对象字面量创建原型方法,这样做会重写原型链。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function SuperType(){
this.property = true;
}
SuperType.prototype.getSuperValue = function(){
return this.property;
}
function SubType(){
this.property = false;
}
SubType.prototype = new SuperType;
//为原型使用字面量添加新方法,会使之前的方法无效
SubType.prototype = {
getSubValue : function(){
return this.subproperty;
}
SomeOtherMeshod :funcion(){
return false;
}
}
var instance = new SubType();
alert(instance.gerSuperValue()); //错误,找不到该方法

原型链的问题:原型链上所有属性都被共享,原型链上的数据可由原型push进行增加。创建子类型的实例时,无法向超类型的构造函数中传递参数。

借用构造函数

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

1
2
3
4
5
6
7
8
9
10
11
function SuperType(){
this.colors = ["red","blue","green"];
}
function SubType(){
SuperType.call(this);
}
var instance1 = new SubType();
instance1.colors.push("black");
console.log(instance1,colors); //red,blue,green,black
var instance2 = new SubType();
console.log(instance2.colors); //red,blue,green

由此可见,因为call()方法创造了一个新的SuperType的作用域,使得两个实例在不同的作用域中进行,从而产生了不同的colors结果。

文章目录
  1. 1. Array类型的数组数量问题
  2. 2. join()方法用于给数组提供分隔符以构建字符串
  3. 3. 栈方法(LIFO):后进先出;队列方法(FIFO):先进先出
  4. 4. 重排序方法
  5. 5. 操作方法
  6. 6. 迭代方法
  7. 7. 归并方法
  8. 8. 函数声明提升
  9. 9. 使用arguments.callee()消除紧密耦合
  10. 10. 函数的prototype属性
  11. 11. 基本包装类
  12. 12. 数值类型的转换
  13. 13. typrof()和instanceof()得到的结果不同,如下:
  • 面向对象
    1. 1. 数据属性
    2. 2. 访问器属性
  • 工厂模式
  • 构造函数模式
  • 原型模式
  • 混成模式
  • 继承
  • 借用构造函数
  • |