JavaScript语言精粹的读书笔记(二)

第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章 代码风格

简短的说了一些代码风格。使代码尽可能地易于阅读。

  1. 结构化的语句里,始终适用代码块,可以减少出错的概率。
  2. 把 { 放在一行的结尾而不是下一行的开头,因为它会避免JavaScript的return语句中的一个设计错误。
  3. 对一个脚本应用或工具库,只用一个全局变量。每个对象都有它自己的命名空间,多以很容易用对象去管理代码。使用闭包能提供近一步的信息隐藏,增强模块的健壮性。

第10章 优美的特性

精简的JavaScript里都是好东西。

  1. 函数是顶级对象
    函数时有词法作用域的闭包。
  2. 基于原型继承的动态作用域
    对象是无类别的,我们可以通过普通的赋值给任何对象增加一个新成员属性。一个对象可以从另一个对象继承成员属性。
  3. 对象字面量和数组字面量
    这对创建新的对象和数组来说是一种非常方便的表示法。JavaScript字面量是数据交换格式JSON的灵感之源。
发布了10 篇原创文章 · 获赞 1 · 访问量 6970

猜你喜欢

转载自blog.csdn.net/eva_feng/article/details/84886506