变 量
1.声明了变量却未赋值,则该变量的值实际上是undefined
2.如果重新声明了变量,其值还是之前的值,并不会丢失
3.数据类型
数字、字符串、布尔值、数组、对象、null、undefined
4.JavaScript区分大小写
5.可用一个var声明或定义多个变量,用逗号隔开
6.JavaScript中可用var声明变量直接使用,程序将其解释为一个全局变量;但最好养成使用变量前声明的习惯
7.变量可作为两种类型的值:
(1)原始值:存储在栈中,即变量访问的位置存储了实际的值;原始值包括:Number、String、Boolean、Undefined、Null
引用值:实际值存储在堆中,存储在变量处的是一个指针,指向存储在堆中的实际值(对象)
(2)可用typeof 运算符来检测一个变量的具体类型,返回值
undefined - 如果变量是 Undefined 类型的
boolean - 如果变量是 Boolean 类型的
number - 如果变量是 Number 类型的
string - 如果变量是 String 类型的
object - 如果变量是一种引用类型或 Null 类型
null被看做是对象的占位符,但仍然是原始类型值
(3)undefined表示声明了却未初始化的变量值
null表示尚未存在的对象,例如某函数返回对象,但查找不到该对象时返回null
使用null==undefined时返回true,但实际上两个是不同意义的值
(4)所有Number类型的值都应该处于Number.MAX_VALUE和Number.MIN_VALUE之间,如果计算生成的值超出这个范围,将会被赋予为无穷大: Number.NEGATIVE_INFINITY和 Number.POSITIVE_INFINITY,这两个的值分别为-Infinity和Infinity;方法isFinite(Num)用于判断一个数是否为无穷。以上特殊值是Number对象的静态属性。
NaN值表示not a number,通常发生在其他类型转换为数字失败时生成的值,它和任何值都不想等,及时自身NaN==NaN为false,使用isNaN(X)来判断是否不是数字
(5)String是唯一没有固定大小的原始值
8.类型转换
<1> Number、Boolean、String被看做伪对象,因此有属性和方法;所有对象和伪对象都有.toString()方法
(1)Number类型的toString()方法有两种使用:
无参数,则输出为10进制的字符串
参数指定输出的进制,例如iNum.toString(2),输出为2进制的字符串;16进制常用于处理HTML颜色
(2)数组的toString()方法将数组转换为字符串并返回该字符串
(3)Boolean的该方法将原布尔类型的变量转换为字符串,并返回字符串“true”或“false”
(4)Date类型变量.toString()
<2>parseInt(x),parseFloat(x),将非数字转换为数字,并返回该数字;只转换第一个无效字符之前的字符串
只能转换String类型变量,其他类型变量均返回NaN;只解析数字部分,其他部分丢弃;parseInt()可在第二个参数指定进制,不指定则根据字符串以什么开头来决定按什么进制转换,返回10进制的数
若字符串中第一个字符不是数字,则parseInt和parseFloat会返回NaN,不再继续扫描下面的字符;
字符串中开头和结尾的空格是允许的
<3>强制类型转换
Number(x):转换整个值,如果整个值能转换才可转,否则返回NaN;能转,会自动判断使用parseInt()还是parseFloat()来转换
true转换为1,false转换为0
String(x):该方法对 null 和 undefined 值强制类型转换可以生成字符串而不引发错误,而obj.toString()对这两种类型会报错
Boolean(x):当要转换的值是至少有一个字符的字符串、非 0 数字或对象时,Boolean() 函数将返回 true。如果该值是空字符串、数字 0、undefined 或 null,它将 返回 false。
9.引用类型
(1)创建对象:var o=new Object(),有一组方法,例如valueOf(),返回最适合该对象的原始值
(2)Boolean对象:是Boolean原始类型的引用类型,覆盖Object的方法,var b = new Boolean(true);
(3)Number对象:是Number原始类型的引用类型
var oNumberObject = new Number(68);<pre name="code" class="javascript">var iNumber = oNumberObject.valueOf();//返回数字对象的Number原始值
toFixed()、toExponential() 和 toPrecision()方法
应避免使用Number对象,尽量使用原始值
(4)String对象
var s = new String("")
有很多方法点击打开链接,但都是String对象不变,生成新的字符串原始值
String对象的所有属性和方法都可用于原始值上,因为原始值是伪对象
10. instanceof运算符用于识别当前对象是哪个原型对象类型,object instanceof prototype constructor
运算符
11.一元
(1)delete o.property/function :删除对象的某个属性/方法
(2)void:常用于避免输出不应该输出的值,使用该操作符返回无效值undefined。例如代码
<a href="javascript:window.open('about:blank')">Click me</a>window.open()返回新窗口的引用,然后该对象被转换成能显示的字符串:object window,刷新原来的页面,避免该情况使用void:
<a href="javascript:void(window.open('about:blank'))">Click me</a>此时返回undefined是无效值,不会在浏览器窗口中显示(没有返回值的函数其实返回了值undefined)
NOT:~num 即数字取负后再减一
与: &
或:|
XOR:^ 当位上的数不同时返回1
左移位:<< 右移位:>>
13.逻辑运算符
抽象操作ToBoolean将其参数按如下规则转换为布尔值:
类型 | 逻辑值 |
undefined | false |
null | false |
number | 0、NaN为false其余为true |
string | ""false,否则为true |
object | true |
boolean | 为其实际值 |
各类型值之间做逻辑运算的规则:点击打开链接
&&、||都是短路运算
14.比较运算符
(1)字符串应用比较运算符时时对应位置的字符代码(ascii码)进行比较,由于大写字母代码比小写字母小,因此应当将字符串同一转换成小写,s.toLowerCase()
(2)比较字符串数字也是解读为字符代码来比价,例如“25”<"3",返回true
15.等性运算 比较规则
(1)==和!= 处理原始值
(2)===和!==处理对象
不进行类型转换的比较
16.条件运算符
variable = boolean_expression ? true_value : false_value;17.逗号运算符
运算级别最低
一般形式为:表达式1,表达式2,,,,表达式n
求解过程为:先计算表达式1,再计算表达式2,,,一直到表达式n。最后整个逗号表达式的值是表达式n的值
语句
18.标签
常和break或continue结合使用,返回到代码中特定位置,尤其在多层嵌套返回最外层嵌套时
var iNum = 0; outermost: for (var i=0; i<10; i++) { for (var j=0; j<10; j++) { if (i == 5 && j == 5) { break outermost; } iNum++; } } alert(iNum); //输出 "55"
var iNum = 0; outermost: for (var i=0; i<10; i++) { for (var j=0; j<10; j++) { if (i == 5 && j == 5) { continue outermost; } iNum++; } } alert(iNum); //输出 "95"
19.如果函数没有返回值或者是没有参数的return语句,则实际返回的是undefined
20.arguments对象可访问函数参数,替代参数名。不会检查传递给函数的参数个数是否等于定义的参数个数,遗漏的参数会以undefined传递给函数,多于的会被忽略
arguments.length可获取参数个数;因此可接受任意多个参数(最多25个)
用arguments对象判断传递给函数的参数个数可模拟函数重载:
function doAdd() { if(arguments.length == 1) { alert(arguments[0] + 5); } else if(arguments.length == 2) { alert(arguments[0] + arguments[1]); } } doAdd(10); //输出 "15" doAdd(40, 20); //输出 "60"21.函数实际上是一个对象(属于引用类型),函数名为指向该对象的指针,是一个变量,因此可以作为值赋给别的变量或作为参数传递;
所有函数都是Function类的实例
22.函数名.length:为函数期望的参数个数(定义时候的参数个数)
函数名.valueOf()和toString()都是返回函数的源代码,常用于调试
对象
23.当一个对象不再被使用时,将其所有引用置为null是个好习惯,以便被销毁
24.JavaScript是晚绑定:编译器或解释程序在运行前,不知道对象的类型。使用晚绑定,无需检查对象的类型,只需检查对象是否支持属性和方法即可。ECMAScript 中的所有变量都采用晚绑定方法。这样就允许执行大量的对象操作,而无任何惩罚。???
25.JavaScript对象
(1)本地对象
独立于宿主环境的由ECMAScript实现提供的对象,例如Object、String、Number、Date、RegExp等
(2)内置对象
独立于宿主环境的由ECMAScript实现提供的对象,且使用前无需实例化(已经被实例化了的),例如Math、Global对象
(3)宿主对象
所有非本地对象都是宿主对象,由 ECMAScript 实现的宿主环境提供的对象。所有BOM和DOM都是宿主对象
26.object的prototype使得可以向对象添加属性和方法:
function employee(name,job,born) { this.name=name; this.job=job; this.born=born; } var bill=new employee("Bill Gates","Engineer",1985); employee.prototype.salary=null; //函数也是一个对象 bill.salary=20000;
document.write(bill.salary);
*****************本地对象*************************
27.字符串对象
(1)str1.localeCompare(str2):用本地的顺序来比较两个字符串,str1<str2返回小于0的数,,,,,;若果用<>符号来比较,则是按unicode编码来比较字符串;而localeCompare则考虑了默认的本地规则,提供比较字符串的方法:
var astr = new Array("hello","big","happy","pretty"); var newastr = astr.sort(function(a,b){return a.localeCompare(b)}); alert(astr.valueOf()); //astr已经改变了,是排序后的数组 alert(newastr.valueOf());
请记住对字符串做出的所有改变都是返回了一个新的字符串而非改变原来的字符串
(2)常用方法
<1> concat()
连接字符串
<2> str.slice(start,end)
返回指定起始终止位置的子字符串,start、end接受负数,表示倒数第几个;start处字符包括,end处字符不包括
str.subString(start,end)
不接受负参数;后不包括
str.subStr(start,length)
start可为负数,不建议使用该方法
<3> match():
参数为字符串,则返回匹配的子字符串,没有则返回null
参数为正则表达式,若有匹配项则返回由匹配项组成的数组(逗号隔开元素),没有则返回null;匹配项的个数取决于正则表达式的属性
var str="1 plus 2 equal 3" document.write(str.match(/\d+/g))
indexOf()、lastIndexOf():返回匹配的位置
search():返回匹配的起始位置;接受正则表达式,忽略大小写追加 /i
<4>replace(substring/regrex,replacement)
参数为子字符串或正则表达式
替换的项数取决于正则表达式中的属性,若为g则替换所有,否则只替换第一个匹配项
replacement可为字符串或函数,在这种情况下,每个匹配都调用该函数,它返回的字符串将作为替换文本使用。?????
replacement字符串中可插入特殊字符,????
<5>split()
若要把单词分割为字母,第一个参数(分割符)使用“”;接受正则表达式
28.数组对象
常用方法
(1)join(separator)
使用指定分割符(可选)将数组元素连接为一个字符串
(2)slice(start,end)
返回指定起始终止(不包括)位置的元素,是返回了一个新数组,原数组不变
(3)splice(index,length,intem1,item2,,,)
index:必需,指定删除/添加项目的位置
length:必需,指定删除的项目数量,为0则不删除,仅添加
item:可选,添加的新项目
数组被改变!!!而非生成一个新数组
29.RegExp对象
(1)直接量:/pattern/attribute
创建对象:new RegExp(pattern,attribute) 参数为字符串
attribute包含属性:g 全局匹配;i 执行对大小写不敏感的匹配;m 多行匹配。如果pattern是一个正则表达式,则要省略该参数
(2)正则表达式一
正则表达式也可以赋给一个变量,var patt = /\w/g;
<1>方括号 []
用于查找某个范围内的字符
<2>元字符 \
查找拥有特殊含义的字符
\b:匹配出现在单词开头或结尾的指定字符串,例如/\bor/:出现在单词开头的or是匹配的,/or\b/:出现的单词结尾的or是匹配的;并不匹配任何一个字符,匹配的是 位置
\B:匹配不是出现在单词开头或结尾的指定字符串,例如/\Bor/:匹配不是出现在单词开头的or,/or\B/:匹配不是出现在单词结尾的or
<3> 量词
(3)正则表达式二
正则表达式入门
分组正则表达式,子表达式用括号括起来,表示整个表达式重复,如匹配IP地址:
var str1 = "history 10.10.127.1 hello"; var patt = /(\d{1,3}\.){3}\d{1,3}/; document.write(str1.match(patt)+"<br />"); //有问题!!!!匹配特殊字符时使用转义字符\
(4)RegExp对象方法
<1> RegExpObj.exec(string):
同match方法类似,返回一个数组,数组中的元素为匹配的项;若正则表达式有分组,则第一个元素为与第一个子表达式相匹配的项,第二个元素为与第二个子表达式相 匹配的项,依次类推;
当RegExpObj无全局属性时,与String对象的match方法相同,只返回第一个匹配的子串;
当有全局属性时,则不同了,match方法返回的数组包含了所有相匹配的串,而exec仍只返回一个,其属性lastIndex指向已匹配文本最后一个字符的下一个字符,这就是 说,您可以通过反复调用 exec() 方法来遍历字符串中的所有匹配文本。当 exec() 再也找不到匹配的文本时,它将返回 null,并自动把 lastIndex 属性重置为 0。
var str = "Visit W3School"; var patt = new RegExp("W3School","g"); var result; while ((result = patt.exec(str)) != null) { document.write(result); document.write("<br />"); document.write(patt.lastIndex); }
如果在一个字符串中完成了一次模式匹配之后要开始检索新的字符串,就必须手动地把(同一个RegExpObj的) lastIndex 属性重置为 0。注意:不具备全局属性的RegExp对象没有lastIndex属性。
<2>RegExpObj.test(string)
测试字符串string中是否含有匹配项,有则返回true,无则返回false
********************内置对象************************
30.已经实例化了的对象,使用前无需再实例化;内置对象也是本地对象
(1)全局对象
包括一些对URI或字符串进行编码解码的方法;
eval(string):用于计算和执行字符串中包含的js代码,返回计算的结果;字符串需为原始字符串而非对象
(2)Math对象
对象作用域
31.this 关键字
用在对象的方法中,总是指向调用该方法的对象;
当不能确定创建的对象的名字时,使用this可在多个地方重用同一个函数,例如:
function showColor() { alert(this.color); }; var oCar1 = new Object; oCar1.color = "red"; oCar1.showColor = showColor; var oCar2 = new Object; oCar2.color = "blue"; oCar2.showColor = showColor; oCar1.showColor(); //输出 "red" oCar2.showColor(); //输出 "blue"
当要创建多个具有相同属性和方法(值可以不同)的时候,有一下几个解决方案
32.工厂模式
(1)创建并返回特定对象的方法,调用此方法创造出对象,为其添加属性和方法,再返回该对象:
function createCar() { var oTempCar = new Object; oTempCar.color = "blue"; oTempCar.doors = 4; oTempCar.mpg = 25; oTempCar.showColor = function() { alert(this.color); }; return oTempCar; } var oCar1 = createCar(); var oCar2 = createCar();(2)还可以给工厂方法添加参数,有点像类的带参构造函数
function createCar(sColor,iDoors,iMpg) { var oTempCar = new Object; oTempCar.color = sColor; oTempCar.doors = iDoors; oTempCar.mpg = iMpg; oTempCar.showColor = function() { alert(this.color); }; return oTempCar; } var oCar1 = createCar("red",4,23); var oCar2 = createCar("blue",3,25); oCar1.showColor(); //输出 "red" oCar2.showColor(); //输出 "blue"
以上方式的缺点是重复地创建函数,为每个对象都创建了自己的函数,但这些函数实际上是相同的,可以将该函数放到工厂方法外面,用一个属性指向该方法:
function showColor() { alert(this.color); } function createCar(sColor,iDoors,iMpg) { var oTempCar = new Object; oTempCar.color = sColor; oTempCar.doors = iDoors; oTempCar.mpg = iMpg; oTempCar.showColor = showColor; return oTempCar; } var oCar1 = createCar("red",4,23); var oCar2 = createCar("blue",3,25); oCar1.showColor(); //输出 "red" oCar2.showColor(); //输出 "blue"这种方法的语义性比较差,看上去不像是对象的方法。
构造函数应该大写,与工厂方法的区别是在构造函数内没有创建对象,而是使用this关键字(this关键字引用当前创建的对象),使用new运算符运行构造函数时,在执行第一句话前先创建一个对象,只能用this才能访问该对象,赋予this属性。默认函数返回this,因此可以不必写出return语句。
function Car(sColor,iDoors,iMpg) { this.color = sColor; this.doors = iDoors; this.mpg = iMpg; this.showColor = function() { alert(this.color); }; } var oCar1 = new Car("red",4,23); var oCar2 = new Car("blue",3,25);
使用new创建对象时经经历了如下几个步骤:
创建一个新对象;
将构造函数的作用域赋给新对象(this指向了该新对象);
执行构造函数中的代码(给对象添加属性);
返回新对象。
使用构造函数可用instanceof或obj.constructor属性来检测对象的类型(推荐使用前一种方式),这是构造函数方式优于工厂模式的地方
该方式同样会重复生成函数:每创建一个实例对象都创建了一个Function的实例(不同对象的function实例不同),相当于 this.showColor = new Function("alert(this.color)");//与函数声明在逻辑上是等价的。所有不同实例的同名函数不是同一个函数对象。创建完成同样任务的不同Function实例没有必要。
可像工厂那样将方法声明移到构造函数外面,再把方法的引用赋给构造方法的属性,但本应是对象的方法就变成全局函数了,封装性差了。
34.原型模式
function Car() { } Car.prototype.color = "blue"; Car.prototype.doors = 4; Car.prototype.mpg = 25; Car.prototype.showColor = function() { alert(this.color); }; var oCar1 = new Car(); var oCar2 = new Car();原型的所有属性都被立即赋予创建的新对象,所有Car的实例都存放了指向showColor函数的指针,避免了重复生成函数的问题。还可以用此方式判断实例的类型:oCar1 instanceof Car
此方式不接受参数,同时当类的属性是一个对象时,会出问题:
function Car() { } Car.prototype.color = "blue"; Car.prototype.doors = 4; Car.prototype.mpg = 25; Car.prototype.drivers = new Array("Mike","John"); Car.prototype.showColor = function() { alert(this.color); }; var oCar1 = new Car(); var oCar2 = new Car(); oCar1.drivers.push("Bill"); alert(oCar1.drivers); //输出 "Mike,John,Bill" alert(oCar2.drivers); //输出 "Mike,John,Bill"所有实例都共享对象属性(每个实例都持有一个指向同一个对象的引用)。
35.混合的构造函数/原型方式
所有非函数属性都由构造函数定义,函数属性由原型定义,这样使得所有函数只被创建一次,每个实例拥有自己的对象属性
function Car(sColor,iDoors,iMpg) { this.color = sColor; this.doors = iDoors; this.mpg = iMpg; this.drivers = new Array("Mike","John"); } Car.prototype.showColor = function() { alert(this.color); }; var oCar1 = new Car("red",4,23); var oCar2 = new Car("blue",3,25); oCar1.drivers.push("Bill"); alert(oCar1.drivers); //输出 "Mike,John,Bill" alert(oCar2.drivers); //输出 "Mike,John"36.动态原型方法
35的封装性不太好,动态原型方法和35很相似,唯一不同是赋予方法属性的位置
function Car(sColor,iDoors,iMpg) { this.color = sColor; this.doors = iDoors; this.mpg = iMpg; this.drivers = new Array("Mike","John"); if (typeof Car._initialized == "undefined") { Car.prototype.showColor = function() { alert(this.color); }; Car._initialized = true; } }使用标志_initialized来判断是否给原型赋予了方法,若果没有则按原型方式赋予方法属性,再给标志设为true,后面则无需再创建该方法了。
目前使用得最广的是混合的构造函数/原型模式,动态原型也使用较广泛
37.使用+连接字符串资源消耗大,可将要连接的字符串放到数组中,再调用join方法连接,可创建一个StringBuffer类打包该功能:
function StringBuffer () { this._strings_ = new Array(); } StringBuffer.prototype.append = function(str) { this._strings_.push(str); }; StringBuffer.prototype.toString = function() { return this._strings_.join(""); };
修改对象
38.可以通过prototype给已有的类增加属性:
Number.prototype.toHexString = function(){ return this.toString(16); }; var inum = 20; var inumHex = inum.toHexString();
39.如果想给每个本地对象添加新方法,需给Object对象添加prototype属性;因为所有本地对象都继承了Object对象
40.还可以通过prototype重定义已有的方法,通常安全的做法是将原来的方法存放到一个指针中再重定义,有可能后面会用到原来的方法
41.早绑定和晚绑定
绑定是指类和对象间建立关系。
早绑定是说实例对象被创建前就应该定义好其方法和属性,这样编译器可提前转换机器代码,例如Java;在对象声明的时候就知道了对象是什么类型,其具有哪些方法和属性
晚绑定则是运行前无需检查对象的类型,只检查是否支持某些方法或属性,要用的时候才把属性和方法赋给对象,javascript支持完绑定和极晚绑定:类型在创建了对象以后又通过prototype增加了属性,在实例对象需要用到该属性时仍可以成功使用
JavaScript继承机制
42.基类
(1)本地类和宿主类不能作为基类,只有开发者自定义的类才能作为基类
如果基类不能被直接使用,而只是给子类提供一系列通用的方法,叫做抽象类
所有方法属性都是公有的(JavaScript中没有私有属性和方法),因此子类可以访问从超类中继承来的所有属性和方法
(2)继承的方式
一. 对象冒充
<1>父类作为子类的方法
思路:基类ClassA的构造函数中定义了一系列属性和方法(用this关键字),将A的构造函数作为子类ClassB的属性,则B就会收到A的方法和属性;通过这种方式子类的所 有属性是自己的属性(包括对象等属性),和父类、其他子类是相隔离的。
应该删除对A的构造函数的引用后再定义新属性和新方法,否则可能复写超类的相关属性和方法(这里的意思应该是把A类中的也改变了吧)。
function ClassA(color){ this.color = color; this.sayColor = function(){ alert(this.color); } } function ClassB(color,name){ this.newMethod = ClassA; //将父类的构造函数引用作为子类的一个属性(方法),其实就是获取了某个普通函数的引用 this.newMethod(color); //在子类中调用该方法,函数中的this是当前新创建的对象 delete this.newMethod; //删除属性,以后不再使用父类的属性和方法 this.name=name; this.sayName = function(){ alert(this.name); } } var oa = new ClassA("red"); var ob = new ClassB("blue","Toyota"); oa.sayColor(); ob.sayColor(); ob.sayName();
以下代码在子类B中重写了A类的方法,但A类仍保持原有的方法不变:
function ClassA(sColor){ this.color = sColor; this.mood = new Array("happy","sad"); this.sayColor = function () { alert(this.color); }; this.sayMood = function(){ alert(this.mood.toString()); } } function ClassB(sColor,sName){ this.fromA = ClassA; this.fromA(sColor); delete this.fromA; this.name = sName; this.sayName = function(){ alert(this.name); } this.sayColor = function(){ alert("overide in B :"+this.color); } } var objA = new ClassA("red"); objA.sayColor(); var objB = new ClassB("blue","Lily"); objB.sayColor(); objA.sayColor();
使用对象冒充可实现多重继承
function ClassZ() { this.newMethod = ClassX; this.newMethod(); delete this.newMethod; this.newMethod = ClassY; this.newMethod(); delete this.newMethod; }问题: 如果存在两个类 ClassX 和 ClassY 具有同名的属性或方法,ClassY 具有高优先级。因为它从后面的类继承。除这点小问题之外,用对象冒充实现多重继承机制轻而易举。
<2>call方法
Function对象新增加了call方法:fun.call( thisObj,para1,para2,,,)
call 方法可将一个函数的对象上下文从初始的上下文改变为由 thisObj 指定的新对象。如果没有提供 thisObj 参数,那么 Global 对象被用作 thisObj。即可将当前this对象切换为别的对象,后面的para1,para2,,,,参数列表是fun的参数。实现在一个类(子类)中使用另一个类(父类)的属性和方法。
使用call方法实现继承,将子类对象传递给call,即可替换父类中的this,随之可使用父类中的方法和属性。使用该方式与冒充对象非常类似,只需把对属性赋值等语句替换为call语句:
function ClassA(sColor){ this.color = sColor; this.mood = new Array("happy","sad"); this.sayColor = function () { alert(this.color); }; this.sayMood = function(){ alert(this.mood.toString()); } } function ClassB(sColor,sName){ ClassA.call(this,sColor); //用新创建的ClassB对象this替换ClassA中的this this.name = sName; this.sayName = function(){ alert(this.name); } this.sayColor = function(){ //仍然可重写父类中的方法 alert("overide in B :"+this.color); } } var objA = new ClassA("red"); objA.sayColor(); var objB = new ClassB("blue","Lily");//new运算符调用B的构造函数创建了一个B实例,在执行B的构造函数时,运行到call,用新生成的实例对象替换了父类构造函数中的
this,并执行父类构造函数,即为新的子类实例对象添加属性 objB.sayColor(); objA.sayColor();
<3>apply方法与call方法类似,不过call中的thisObj之后是参数列表,而apply中是将参数放到数组中
function ClassB(sColor, sName) { //this.newMethod = ClassA; //this.newMethod(color); //delete this.newMethod; ClassA.apply(this, new Array(sColor)); this.name = sName; this.sayName = function () { alert(this.name); }; }call和apply讲解
prototype 对象是个模板,要实例化的对象都以这个模板为基础,prototype 对象的任何属性和方法都被传递给那个类的所有实例。原型链利用这种功能来实现继承机制。
用父类的实例来替换子类的整个prototype对象,避免了挨个设置子类的prototype属性;此时注意该步骤应该在为子类添加新的prototype属性之前,如果添加新属性在替换整个prototype对象之前,则添加的新属性都会被“抹去”。
注意:原型链中要确保构造函数没有参数,这是原型链的标准做法:
function BaseA(){} BaseA.prototype.color = "red"; BaseA.prototype.sayColor=function(){ alert(this.color); } function ChildB(){} ChildB.prototype = new BaseA(); //创建了类A的实例,该实例获取了类A的prototype对象持有的所有属性和方法,再将该实例赋值给B类的prototype对象,即B类的原型对象是类A的一个实例 //可访问其属性和方法 ChildB.prototype.name = "Mike"; ChildB.prototype.sayName=function(){ alert(this.name); } var objb = new ChildB(); objb.color = "blue"; objb.sayColor(); objb.sayName();
在原型链中,对创建的子类实例用运算符instanceof进行测试,对子类和父类都返回true;
原型链不支持多重继承,因为其prototype会被后一个父类实例覆盖;
这种方式和使用原型进行创建类有相同的问题,就是对于并不想要共享的属性也放到了prototype对象中,这样由一个对象来修改某个属性,其他对象包括父类的该属性也跟着变了。
三. 混合方式
冒充对象必须使用构造函数形式,原型链又不能使用带参函数,因此可将两者结合。与创建类类似,最好的方式是属性由构造函数创建,方法由原型实现,在继承中也如此,父类的属性由构造函数创建,方法由原型方式实现;子类的属性由构造函数实现,其中继承的父类的属性部分由对象冒充实现,子类的继承父类的方法由原型对象prototype(原型链)实现:
//属性由构造函数创建,方法由原型方式实现 function BaseA(color){ this.color = color; } BaseA.prototype.sayColor=function(){ alert(this.color); } function ChildB(color,name){ BaseA.call(this,color); //对象冒充继承父类的属性 this.name=name; } ChildB.prototype=new BaseA(); //继承父类A的所有原型属性,这里是方法 ChildB.prototype.sayName=function(){ //继续用原型方式添加新的方法 alert(this.name); } var objA = new BaseA("red"); objA.sayColor(); var objB = new ChildB("blue","Lily"); objB.sayColor(); objB.sayName();
JavaScript使用原型链实现继承的理解:阮一峰日志
摘要:
1.用new运算符来创建实例对象,不过new后面不像普通的面向对象语言跟的是类,而是一个构造函数
2.但是构造函数使得属性和方法不能共享,每个实例创建的时候都是自己的属性和方法,修改自身的不会影响其他对像的,这样对于需要共享的属性和方法,但是却由每个实例自己再生成一遍,造成了资源浪费,由此为对象引入了prototype属性,这个属性包含一个对象叫做prototype对象,对于需要共享的方法和属性(共享的属性可以理解为各实例的该属性都具有相同值)放到prototype里,不需要共享的放到构造函数里
对象一但被创建会自动引用prototype对象的属性和方法
3.所有对象都共享同一个prototype对象,在外界看来prototype对象就像是实例的原型,而实例对象就像是继承了prototype对象一样