第5章 继承
伪类
JavaScript的原型存在诸多矛盾。它不直接让对象从其他对象继承,反而插入了一个多余的间接层:通过构造器函数产生对象。
当一个函数对象被创建时,Function构造器产生的函数对象会运行类似这样的一些代码:
this.prototype = {constructor:this}
新函数对象被赋予一个prototype属性,它的值是一个包含 constructor 属性且属性值为该新函数的对象。这个prototype对象是存放继承特征的地方。
使用构造器函数存在一个严重的危害,如果在调用构造器函数时忘记了在前面加上new前缀,那么this将不会被绑定到一个新对象上。this将被绑定到全局对象上,会破坏全局变量环境。
所有构造器函数都约定命名成首字母大写的形式。一种更好的备选方案就是根本不使用new 。
原型
基于原型的继承相比基于类的继承在概念上更为简单,一个新对象可以继承一个旧对象的属性。
通过构造一个有用的对象开始,利用Object.create方法构造出更多的实例。
例子:
//原型对象
var myMammal = {
name: ' Herb the Mammal',
get_name: function(){
return this.name;
},
says: function(){
return this.saying || "";
}
}
console.log(myMammal.get_name()) // Herb the Mammal
//构建实例,差异化继承
var myCat = Object.create(myMammal);
myCat.name = 'Henrietta';
myCat.saying = 'meow';
myCat.purr = function(n){
var i, s ='';
for (var i = 0; i < n; i++) {
if(s){
s += '-';
}
s += 'r';
};
return s;
};
myCat.get_name =function(){
return this.says() + ' '+ this.name + ' ' + this.says();
}
console.log(myCat.get_name()) // meow Henrietta meow
关于继承的相关知识点,可参见:JS实现继承的几种方式
第6章 数组
数组是一段线性分配的内存,它通过整数计算偏移并访问其中的元素,数组是一种性能出色的数据结构。不幸的是,JavaScript没有像此类数组一样的数据结构。
JavaScript提供一种拥有一些类数组特性的对象。它把数组的下标转变成字符串,用其作为属性。
数组字面量
数组继承自Array.prototype。而对象字面量是继承自Object.prototype。
长度
每个数组都有一个length属性。数组可以直接设置length的值。设置更大的length不会给数组分配更多的空间,而设小导致所有下标大于等于新length的属性被删除。通过下标指定为一个数组的当前length,可以附加一个新元素到该数组的尾部:arr[arr.length],也可以用push方法
var numbers = [1,2,3];
console.log(numbers.length); // 3
numbers[numbers.length] = 10;
console.log(numbers) // [1, 2, 3, 10]
numbers.push('test')
console.log(numbers) // [1, 2, 3, 10, "test"]
删除
delete运算符可以用来从数组中移出元素,但是会在数组中留下一个空洞,这是因为排在删除元素之后的元素保留它们最初的属性。
可以用splice(数组中的一个序号,要删除的元素个数),因为被删除属性后的每个属性必须被移除,并且以一个新的键值重新插入,这样对于大型数组效率不高。
delete numbers[1];
console.log(numbers) // [1, empty, 3, 10, "test"]
numbers.splice(1,2);
console.log(numbers) // [1, 10, "test"]
容易混淆的地方
JavaScript本身对于数组和对象的区别是混乱的,利用typeof运算符判断类型
typeof [] === "object"; // true
typeof {} === "object"; // true
当属性名是小而连续的整数时,应使用数组,否则使用对象。判断一个对象否为数组:
// 定义is_array函数来判断
var is_array = function(value){
return value &&
typeof value === 'object' &&
value.constructor === Array;
}
// is_array此方法它在识别从不同窗口(window)或帧(frame)里的构造的数组时会失败。
// 下面这个为更优方案做判断
var is_array =function(value){
return Object.prototype.toString.apply(value) ==='[object Array]'
}
指定初始值
JavaScript的数组通常不会预置值。如果用 [ ]得到一个新数组,它将是空的。访问一个不存在的元素,得到的值则是undefined。
//JS数组指定初始值
Array.dim = function(dimension, initial){
var a= [], i;
for (var i = 0; i < dimension; i++) {
a[i] = initial;
};
return a;
}
//创建一个包含10个 0 的数组
var myArray = Array.dim(10,0);
console.log(myArray) // (10) [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
扩展:ES6中提供了fill来填充。 方法是 array.fill(value, start, end) 第二、第三个参数,用于指定开始填充位置和停止填充位置 (默认为 array.length);
var arr = ['a','b','c'];
console.log(arr.fill('ff') ); // ["ff", "ff", "ff"]
console.log(new Array(3).fill(0)); // [0, 0, 0]
console.log( new Array(3).fill(0,1,2) ); // [empty, 0, empty]
第7章 正则表达式
正则表达式对字符串中的信息实现查找、替换和提取操作。
可处理正则表达式的方法有regexp.exec、regexp.test、string.match、string.search和string.split。通常来说,正则相较于等效的字符串处理有着显著的性能优势。
有两种方法创建一个RegExp对象,优先考虑正则表达式字面量(包围在一对斜杠里), 另一个是使用RegExp构造器,这个构造器接收一个字符串并编译成一个RegExp对象。
一个正则表达式分支包含一个或多个正则表达式序列,这些序列用|分开,一个正则表达式序列包含一个或多个正则表达式因子。
常用的正则表达式字符,可查看表达式手册
在线匹配工具:
1、https://regex101.com/
2、http://rubular.com/
第8章 方法
Array:
- array.concat(item…)
concat方法产生一个新数组,它包含一份array的浅复制并把一个或者多个参数item附加在其后,如果参数是个数组,那么它的每个元素会被分别添加进去。array不变。后面有和它功能类似的array.push(item…)方法。
var a = ['a','b','c'];
var b= ['x','y','z'];
var c = b.concat(a,'true');
console.log(c) //["x", "y", "z", "a", "b", "c", "true"]
// ES6合并数组方法使用的便捷运算符 ...
var d = [...a,true,...b];
console.log(d) //["a", "b", "c", true, "x", "y", "z"]
- array.join(separator)
join 方法把一个array构造成一个字符串;默认的separator是逗号‘,’。如果想做到无间隔的连接。我们可以使用空字符串作为separator。 - array.pop()
pop方法是移除array中的最后一个元素并返回该元素,如果array是empty,它会返回undefined。
var a = ['a','b','c'];
var c= a.pop();
console.log(c) // c
console.log(a) // ["a", "b"]
//pop也可像如下实现:
Array.method('pop', function(){
return this.splice(this.length-1, 1)[0];
});
- array.push(item…)
push方法把一个或多个参数附加到一个数组的尾部,它与concat方法的区别是会修改array;如果参数是一个数组,它会把参数数组作为单个元素整个添加到数组中,并返回这个array的新长度值。
var a = ['a','b','c'];
var b= ['x','y','z'];
var c = b.push(a,'true');
console.log(b) // ["x", "y", "z", ['a','b','c'], "true"]
console.log(c) // 5
//push也可像如下实现:
Array.method('push',function(){
this.splice.apply(this,[this.length, 0].concat(Array.prototype.slice.apply(arguments)));
return this.length;
})
-
array.reverse()
reverse方法反转array里的元素的顺序,并返回array本身; -
array.shift()
shift方法移除数组array中的第1个元素并返回该元素,如果array是empety,它会返回undefined。 shift通常比pop慢很多。
var a = ['a','b','c'];
var c = a.shift();
console.log(c) // 'a'
console.log(a) // ["b", "c"]
//shift也可如下实现:
Array.method('shift', function(){
return this.splice(0,1)[0];
})
- array.slice(start,end)
slice(start, end)方法对array中的一段做浅复制,首先复制array[start],一直复制到array[end]为止(不包含array[end]该元素)。end参数为可选的,默认值为该数组的长度array.length 。如果两个参数中任何一个是负数,array.length会和它们相加。试图让他们成为非负数。如果start大于等于array.length,得到的结果是一个新的空数组 - array.sort(comparefn)
sort方法对array中的内容进行排序;无法正确的给一组数字排序。JS默认比较函数把要排序的元素都视为字符串。
数字按照大小排列可以利用比较函数;比较函数应该接受两个参数,并且如果这两个参数相等则返回0,如果第1个参数应该排列在前面,则返回一个负数,如果第二个参数应该排列在前面,则返回一个正数。
var n = [4,8,15,16,23,42];
console.log(n.sort()); // [15, 16, 23, 4, 42, 8]
// 比较函数 数字a,b,简写的话就是下面的 return a-b/b-a
function compare(a, b) {
if (a < b ) { // compareFunction(a, b) 小于 0 ,那么 a 会被排列到 b 之前
return -1;
}
if (a > b ) { //compareFunction(a, b)大于 0 , b 会被排列到 a 之前
return 1;
}
// compareFunction(a, b) 等于 0 , a 和 b 的相对位置不变。
return 0;
}
var m = n.sort(function(a,b){
return a-b;
})
console.log(m) // [4, 8, 15, 16, 23, 42]
- array.splice(start, deleteCount, item…)
splice方法从array中移除一个或多个元素,并用新的item替换它们;参数start表示从数组中移除元素的开始位置,参数deleteCount是要移除的元素个数,如果有额外的参数,那些item会插入到被移除的位置上。它返回一个包含被移除元素的数组。原数组会被改变。
//splice也可如下实现:
Array.method('splice', function(start, deleteCount){
var max = Math.max,
min = Math.min,
delta,
element,
insertCount = max(arguments.length-2, 0),
k = 0,
len = this.length,
new_len,
result = [],
shift_count;
start = start || 0;
if(start < 0){
start += len;
}
start = max(min(start,len), 0);
deleteCount = max(min(typeof deleteCount === 'number'? deleteCount :len, len-start), 0);
delta = insertCount - deleteCount;
new_len = len + delta;
while(k < deleteCount){
element = this[start + k];
if(element !== undefined){
result[k] = element;
}
k += 1;
}
shift_count = len - start -deleteCount;
if(delta < 0){
k = start + insertCount;
while(shift_count){
this[k] = this[k-delta];
k += 1;
shift_count -= 1;
}
this.length = new_len;
} else if(delta > 0){
k = 1;
while(shift_count){
this[new_len - k] = this[len- k];
k += 1;
shift_count -= 1;
}
this.length = new_len;
}
for (k = 0; i < insertCount; k++) {
this[start+k] = arguments[k+2];
}
return result;
})
- array.unshift(item…)
unshift方法像push方法一样,用于把元素添加到数组中,但它是把item插入到array的开始部分而不是尾部,返回array的新长度length;原数组改变
//unshift也可如下实现:
Array.method('unshift', function(){
this.splice.apply(this, [0,0].concat(Array.prototype.slice.apply(arguments)));
return this.length;
})
Function
- function.apply(thisArg [,argArray])
apply方法调用function,传递一个会被绑定到this上的对象和一个可选的数组作为参数。
B.apply(A, arguments);即A对象应用B对象的方法。
扩展:apply与call的区别
Number
- number.toExponential(fractionDigits)
toExponential方法把number转换成一个指数形式的字符串,可选参数控制其小数点后的数字位数,它的值必须在0~20之间。 - number.toFixed(fractionDigits)
toFixed方法把number转换为一个十进制形式的字符串,可选参数控制小数点后的数字位数,它的值必须在0~20之间,默认值是0。 - number.toPrecision(precision)
toPrecision方法把number转换为一个十进制数形式的字符串,可选参数precision控制数组的精度,它的值必须在1~21。 - number.toString(radix)
toString方法把number转换成为一个字符串,radix控制基数,参数值必须在2~36之间,默认的radix是以10为基数的;在普通情况下,number.toString()可以更简单的写为 String(number)
Object
- object.hasOwnProperty(name)
如果这个object包含名为name的属性,那么返回true。原型链中的同名方法不会被检测。这个方法对name就是“hasOwnProperty”时不起作用。
var a = {member:'Joe'};
var b = Object.create(a);
var t = a.hasOwnProperty('member');
var u = b.hasOwnProperty('member');
var v = b.member;
console.log(t) // true
console.log(u) // false
console.log(v) // Joe
RegExp
- regexp.exec( string )
exec是正则中最强大(和最慢)的方法;如若成功匹配regexp和字符串string,它会返回一个数组。下标为0的元素为整个匹配的字符串;下标为1的元素为分组1捕获的文本,依次类推; 如果匹配失败,返回 null 。 - regexp.test(string)
test是最简单(和最快)的方法。匹配成功,返回true,否则返回false。不要对这个方法使用g标识。
var str="Hello world!";
//查找"Hello"
var patt = /Hello/g;
var result = patt.test(str);
var result2 = patt.test(str);
var result3 = patt.test(str);
console.log("返回值: " + result); // 返回值: true
console.log("返回值: " + result2); // 返回值: false
console.log("返回值: " + result3); // 返回值: true
// test也可如下实现:
RegExp.method('test', function(string){
return this.exec(string) !== null;
});
String
- string.charAt(pos)
返回string中pos位置处的字符,不存在则返回空; - string.charCodeAt(pos)
返回string中pos位置处的字符的字符码位。如果pos小于0或者大于字符串的长度string.length时;它返回NaN - string.concat(string…)
连接字符串,但是用+号运算符更便捷 ; - string.indexof(searching,position)
在string中查找第一个参数,如果被找到返回该字符的位置,不存在则返回-1。position可设置指定位置开始查找。 - string.lastIndexOf(searching,position)
lastIndexOf 方法和indexOf方法类似,只不过该方法是从字符串的末尾查找,但返回的值还是正向的位置值。 - string.localeCompare(that)
比较两个字符串;若string比that小,结果为负数,若相等为0;类似于array.sort比较函数。 - string.match(regexp)
如果没有g标识,则与调用regexp.exec(string)的结果相同;如果带有g标识,那么它生成一个包含所有匹配(除捕获分组之外)的数组。 - string.replace(searchValue,replaceValue)
对string进行查找和替换操作,并返回一个新的字符串。参数searchvalue可以是一个字符串也可以是一个正则表达式对象;若是字符串只会在第一次出现的地方被替换;如果是一个正则表达式并且带有g标识,它会替换所有的匹配,如果没有带g标识,它仅替换第一个匹配。 - string.search(regexp)
返回第一个匹配的首字符位置,不存在返回-1; - string.slice(start,end)
slice方法复制string的一部分来构造一个新的字符串。如果start参数是负数,它将与string.length相加。end参数是可选的。 - string.split(separator, limit)
把string分割成片段来创建一个字符串数组。可选参数limit可以限制分割的片段数量。separator参数可以是字符串或者正则。 - string.substring(start,end)
与slice方法一样,不过它不能处理负数参数。 - string.toLocaleLowerCase()
它使用本地化的规则把这个string中的字母转换成小写格式。 - string.toLocaleUpperCase()
它使用本地化的规则把这个string中的字母转换成大写格式。 - string.toLowerCase()
返回新字符串,所有字母转成小写格式。 - string.toUpperCase()
返回新字符串,所有字母转成大写格式。 - String.fromVharCode(char…)
根据一串数字编码返回一个字符串。
该方法是 String 的静态方法,字符串中的每个字符都由单独的数字 Unicode 编码指定。它不能作为您已创建的 String 对象的方法来使用。
第9章 代码风格
简短的说了一些代码风格。使代码尽可能地易于阅读。
- 结构化的语句里,始终适用代码块,可以减少出错的概率。
- 把 { 放在一行的结尾而不是下一行的开头,因为它会避免JavaScript的return语句中的一个设计错误。
- 对一个脚本应用或工具库,只用一个全局变量。每个对象都有它自己的命名空间,多以很容易用对象去管理代码。使用闭包能提供近一步的信息隐藏,增强模块的健壮性。
第10章 优美的特性
精简的JavaScript里都是好东西。
- 函数是顶级对象
函数时有词法作用域的闭包。 - 基于原型继承的动态作用域
对象是无类别的,我们可以通过普通的赋值给任何对象增加一个新成员属性。一个对象可以从另一个对象继承成员属性。 - 对象字面量和数组字面量
这对创建新的对象和数组来说是一种非常方便的表示法。JavaScript字面量是数据交换格式JSON的灵感之源。