// ECMAScript有两种开发模式:1.函数式(过程化);2.面向对象(OOP);
1 // 创建一个对象,然后给这个对象新的属性和方法;2 var box = new Object(); // 创建一个Object对象;3 box.name = 'lee'; // 创建一个name属性并赋值;4 box.age = 100;5 box.run = function(){ // 创建一个run()方法并返回值;6 return this.name+this.age+'运行中...';7 } 8 console.log(box.run()); // 输入属性和方法的值;9 // 缺点:想创建类似的对象,就会产生大量的代码;
1 // 这种方法就是为了解决实例化对象产生大量代码重复的问题; 2 function createObject(name,age){ // 集中创建函数体; 3 var obj = new Object; // 函数体内创建Object; 4 obj.name = name; 5 obj.age = age; 6 obj.run = function(){ 7 return this.name+this.age+"运行中..."; 8 }; 9 return obj;10 }11 var box1 = createObject("lee",100); // 实例化;调用函数并传参;12 var box2 = createObject("jack",200); // 实例二;13 console.log(box1.run()+box2.run()); // 实例保持相对独立;14 // 缺点:对象与实例的识别问题;无法搞清楚它们到底是那个对象的实例;15 console.log(typeof box1); // Object;
1 // ECMAScript采用构造函数(构造方法)可用来创建特定的对象; 2 function Box(name,age){ // 构造函数模式; 3 this.name = name; // this代表对象Box; 4 this.age = age; 5 this.run = function(){ 6 return this.name+this.age+"运行中..."; 7 }; 8 } 9 var box1 = new Box("lee",100); // 要创建对象的实例必须用new操作符;10 var box2 = new Box("jack",200); // box1和box2都是Box对象的实例;11 console.log(box1 instanceof Box); // true;很清晰的识别box1从属于Box;12 // 使用构造函数,即解决了重复实例化的问题,有解决了对象识别的问题;
1 // 使用构造函数与工厂模式不同之处:2 // (1).构造函数方法没有显示的创建对象(new Object);3 // (2).直接将属性和方法赋值给this对象;4 // (3).没有return语句;
1 // 构造函数规范:2 // (1).函数名(function Box)和实例化构造名(new Box)相同且大写;3 // (2).通过构造函数创建实例对象,必须使用new运算符;
1 // 构造函数和普通函数的区别:2 var box = new Box('lee',100); // 构造模式调用;3 Box('lee',200); // 普通模式调用,无效;4 5 var o = new Object();6 Box.call(o,'jack',200); // 对象冒充调用;7 // 将Box对象作用域扩充到对象o;Box()方法的运行环境已经变成了对象o里;
1 // 构造函数的问题:2 // 使用构造函数创建每个实例的时候,构造函数里的方法都要在每个实例上重新创建一遍;3 // 因为ECMAScript中的函数是对象,因此每定义一个函数,也就是实例化了一个对象;4 // 以这种方式创建函数,会导致不同的作用域链和标识符解析;
// 我们创建的每个函数都有一个PRototype(原型)属性,这个属性是一个对象;
// 用途:包含可以由特定类型的所有实例共享的属性和方法;
// 理解:prototype是通过调用构造函数创建的那个对象的原型对象;
// 使用原型的好处是可以让所有对象实例共享它所包含的属性和方法;
// 也就是说,不必在构造函数中定义对象信息(属性/方法),而是可以直接将这些信息添加到原型中;
1 1.原型模式 2 function Box(){} // 声明构造函数; 3 Box.prototype.name = 'Lee'; // 在原型里添加属性和方法; 4 Box.prototype.age = 100; 5 Box.prototype.run = function() { 6 return this.name+this.age+'运行中...'; 7 }; 8 var box1 = new Box(); 9 var box2 = new Box();10 console.log(box1.run==box2.run); // =>true;方法引用的地址保持一致;11 // 在原型中多了两个属性,这两个原型属性都是创建对象时自动生成的;12 // 1.__proto__:构造函数指向原型对象的一个指针;它的作用:指向构造函数的原型的属性constructor;13
14// IE浏览器在脚本访问__proto__会不能识别;
15 16 // 判断一个实例对象是否指向了该构造函数的原型对象,可以使用isPrototypeOf()方法来测试;17 console.log(Box.prototype.isPrototypeOf(box)); // =>true; 只要实例化对象,即都会指向;18 19 // 原型模式的执行流程:20 // 1.先查找构造函数对象的实例里的属性或方法,若有,立刻返回;21 // 2.若构造函数对象的实例里没有,则去它的原型对象里找,若有,就返回;22 23 // 虽然我们可以通过对象实例访问保存在原型中的值,但却不能访问通过对象实例重写原型中的值;24 var box1 = new Box();25 console.log(box1.name); // Lee; 原型里的值;26 bo1.name = 'jack';27 console.log(box1.name); // Jack;实例自己赋的值;28 var box2 = new Box();29 console.log(box2.name); // Lee;原型里的值;没有被box1修改;30 // 如果想要box1继续访问原型里的值,可以把构造函数里的属性删除即可;31 delete box1.name; // 删除实例自己的属性;32 console.log(box1.name); // Lee; 原型里原来的值;
1 // 如何判断属性是在构造函数的实例里,还是在原型里? 可以用hasOwnProperty()函数来验证;2 console.log(box.hasOwnProperty('name')); // 实例里若有返回true,否则返回false;3 4 // in操作符会在通过对象能够访问给定属性时返回true,无论该属性存在与实例中还是原型中;5 console.log('name' in box); // =>true,存在实例中或原型中;
1 3.更简单的原型语法(原型+字面量模式) 2 function Box(){}; 3 Box.prototype = { // 以字面量形式创建包含属性和方法的新对象; 4 name:'Lee', 5 age:100, 6 run:function(){ 7 return this.name+this.age+'运行中...'; 8 } 9 };10 11 // 使用构造函数创建原型对象和使用字面量创建原型对象在使用上基本相同;12 // 但是,使用字面量创建的原型对象使用constructor属性不会指向实例,而是指向原型对象Object;构造函数的方式则相反;13 var box = new Box();14 console.log(box instanceof Box);15 console.log(box instanceof Object); 16 console.log(box.constructor == Box); // 字面量方式,返回false;17 console.log(box.constructor == Object); // 字面量方式,返回true;18 // 如果想让字面量方式的constructor指向实例对象:19 Box.prototype = {20 constructor:Box, // 直接强制指向即可;21 }22 23 // PS:字面量方式为什么constructor会指向Object?24 // 因为Box.prototype={}这种字面量写法就是创建一个新对象;25 // 而每创建一个函数,就会同时创建它的prototype,这个对象也会自动获取constructor属性;26 // 所以,新对象的constructor重写了Box原来的constructor,因此指向了新对象,27 // 那个新对象没有指定构造函数,那么就默认为是Object;
1 // 原型的声明是有先后顺序的,所以,重写的原型会切断之前的原型; 2 function Box(){}; 3 Box.prototype = { 4 constructor:Box, 5 name:'Lee', 6 age:100, 7 run:function(){ 8 return this.age+'运行中...'; 9 }10 };11 Box.prototype = { // 原型重写了,覆盖了之前的原型;12 age:200,13 run:function(){14 return this.age+'运行中...';15 }16 }17 var box = new Box();18 console.log(box.run()); // =>200运行中...;19 // 重写原型对象切断了现有原型与任何之前已经存在的对象实例之间的联系;对象实例引用的仍然是最初的原型;
1 // 原型对象不仅仅可以在自定义对象的情况下使用,而是ECMAScript内置的引用类型都可以使用这种方式,2 // 并且内置的引用类型本身也是用了原型;3 console.log(Array.prototype.sort); // =>function sort() { [native code] };4 console.log(String.prototype.substring); // =>function substring() { [native code] };
1 // 原型模式创建对象缺点:省略了构造函数传参初始化这一过程,带来的缺点就是初始化的值都是一致的; 2 // 而原型最大的有点就是共享,属性共享; 3 // 但是,如果原型中的属性包含引用类型(对象),共享就会存在一定问题; 4 function Box(){}; 5 Box.prototype = { 6 constructor:Box, 7 name:'Lee', 8 age:100, 9 family:['father','mother'],10 run:function(){11 return this.name+this.age+this.family;12 }13 };14 var box1 = new Box();15 box1.family.push('sister'); // 为box1的family属性添加了sister;而这个属性被共享到原型了;16 console.log(box1.run()); // =>Lee100father,mother,sister;17 var box2 = new Box();18 console.log(box2.run()); // =>Lee100father,mother,sister;19 // 数据共享导致实例化出的数据不能保存自己的特性;
1 // 为了解决构造传参和共享问题,组合构造函数+原型模式: 2 function Box(name,age){ // 不共享的使用构造函数; 3 this.name = name; 4 this.age = age; 5 this.family
新闻热点
疑难解答