经过原型这种机制,JavaScript 中的对象从其余对象继承功能特性;这种继承机制与经典的面向对象编程语言的继承机制不一样.javascript
JavaScript 常被描述为一种基于原型的语言 (prototype-based language)——每一个对象拥有一个原型对象,对象以其原型为模板、从原型继承方法和属性.原型对象也可能拥有原型,并从中继承方法和属性,一层一层、以此类推.这种关系常被称为原型链 (prototype chain),它解释了为什么一个对象会拥有定义在其余对象中的属性和方法.html
准确地说,这些属性和方法定义在 Object 的构造器函数(constructor functions)之上的 prototype 属性上,而非对象实例自己.java
在传统的 OOP 中,首先定义“类”,此后建立对象实例时,类中定义的全部属性和方法都被复制到实例中.在 JavaScript 中并不如此复制——而是在对象实例和它的构造器之间创建一个连接(它是proto属性,是从构造函数的 prototype 属性派生的),以后经过上溯原型链,在构造器中找到这些属性和方法.git
理解对象的原型(能够经过 Object.getPrototypeOf(obj)或者已被弃用的proto属性得到)与构造函数的 prototype 属性之间的区别是很重要的.前者是每一个实例上都有的属性,后者是构造函数的属性.也就是说,Object.getPrototypeOf(new Foobar())和 Foobar.prototype 指向着同一个对象.github
函数能够有属性, 每一个函数都有一个特殊的属性叫做原型(prototype).编程
测试一下数组
function pro(){} pro.inner = "pro-inner" pro.prototype.out = "out function" console.log(pro) var pro2 = function(){} pro2.inner = "pro2-inner" pro2.prototype.out = "var varible" console.log(pro2.prototype)
构造函数广泛使用首字母大写的命名方式,这不是 js 这个语言强制规定的,而是人们在使用的过程当中一种约定成俗浏览器
控制台打印出来的对象安全
out: "var varible" constructor: ƒ () inner: "pro2-inner" length: 0 name: "pro2" arguments: null caller: null prototype: {out: "var varible", constructor: ƒ} __proto__: ƒ () [[FunctionLocation]]: pen.js:7 [[Scopes]]: Scopes[1] __proto__: constructor: ƒ Object() __defineGetter__: ƒ __defineGetter__() __defineSetter__: ƒ __defineSetter__() hasOwnProperty: ƒ hasOwnProperty() __lookupGetter__: ƒ __lookupGetter__() __lookupSetter__: ƒ __lookupSetter__() isPrototypeOf: ƒ isPrototypeOf() propertyIsEnumerable: ƒ propertyIsEnumerable() toString: ƒ toString() valueOf: ƒ valueOf() toLocaleString: ƒ toLocaleString() get __proto__: ƒ __proto__() set __proto__: ƒ __proto__()
使用 new 运算符来在如今的这个原型基础之上,建立一个 proIns 实例.使用 new 运算符的方法就是在正常调用函数时,在函数名的前面加上一个 new 前缀. 经过这种方法,在调用函数前加一个 new ,它就会返回一个这个函数的实例化对象.闭包
var pro2 = function(){} pro2.inner = "pro2-inner" pro2.prototype.out = "var varible" var proIns = new pro2() proIns.prop = "add prop" console.log(proIns) console.log(proIns.prototype) console.log(pro2.prototype)
控制台输出的内容
prop: "add prop" __proto__: out: "var varible" constructor: ƒ () __proto__: Object
proIns 的 proto 属性就是 pro2.prototype,而 pro2.prototype 的proto就是 Object,当须要访问 proIns 的某个属性或者方法的时候,浏览器就会沿着原型链一直向上进行查找是否有该属性/方法.
执行路径:
var Pro = function(){} Pro.prototype.inner="i am pro" var proIns = new Pro() console.log(proIns.__proto__) console.log(proIns.__proto__.__proto__) console.log(proIns.__proto__.__proto__.__proto__)
__proto__
也就是中查找这个属性**(Pro.prototype)**__proto__
没有这个属性, 浏览器就会去查找 proIns 的 __proto__
的 __proto__
的属性**(Object.prototype)**proIns.__proto__.__proto__.__proto__
为空,浏览器判断原型链上不存在该属性,该属性获取 undefined定义一个构造器函数 Person
function Person(name, age, work) { this.name = name; this.age = age; this.work = work; this.study = function() { console.log(this.name + "学习了" + this.work); }; } Person.prototype.eat = function() { console.log(this.name + "依旧须要吃饭"); }; var programmer = new Person("称序员", 42, "coding"); programmer.study(); programmer.eat(); console.log(programmer.toString())
此时输出 programer 在控制台上
Person {name: "称序员", age: 42, work: "coding", study: ƒ} name: "称序员" age: 42 work: "coding" study: ƒ () __proto__: eat: ƒ () constructor: ƒ Person(name, age, work) __proto__: constructor: ƒ Object() __defineGetter__: ƒ __defineGetter__() __defineSetter__: ƒ __defineSetter__() hasOwnProperty: ƒ hasOwnProperty() __lookupGetter__: ƒ __lookupGetter__() __lookupSetter__: ƒ __lookupSetter__() isPrototypeOf: ƒ isPrototypeOf() propertyIsEnumerable: ƒ propertyIsEnumerable() toString: ƒ toString() valueOf: ƒ valueOf() toLocaleString: ƒ toLocaleString() get __proto__: ƒ __proto__() set __proto__: ƒ __proto__()
该案例总共调用了三个方法.过程以下:
原型链中的方法和属性没有被复制到其余对象——它们被访问须要经过前面所说的“原型链”的方式.上面的执行也是先查找 programmer,以后查找
programmer.__proto__
,最后查找programmer.__proto__.__proto__
没有官方的方法用于直接访问一个对象的原型对象=>原型链中的“链接”被定义在一个内部属性中,在 JavaScript 语言标准中用 [[prototype]] 表示(参见 ECMAScript).然而,大多数现代浏览器仍是提供了一个名为 __proto__
(先后各有 2 个下划线)的属性,其包含了对象的原型.
programmer 继承的属性和方法是定义在 prototype 属性之上的(你能够称之为子命名空间 (sub namespace) )——那些以 Object.prototype. 开头的属性,而非仅仅以 Object. 开头的属性.prototype 属性的值是一个对象,咱们但愿被原型链下游的对象继承的属性和方法,都被储存在其中.
因而 Object.prototype.watch()、Object.prototype.valueOf() 等等成员,适用于任何继承自 Object() 的对象类型,包括使用构造器建立的新的对象实例.
Object.is()、Object.keys(),以及其余不在 prototype 对象内的成员,不会被“对象实例”或“继承自 Object() 的对象类型”所继承.这些方法/属性仅能被 Object() 构造器自身使用.
例如
const str = 'today is sunshine'; console.log(str.indexOf(1));
str 就至关于经过 new String()拥有了一些有用的方法
咱们常用的var obj = {}
,经过 Object.create 表示:
var obj = {}; // 以字面量方式建立的空对象就至关于: var obj = Object.create(Object.prototype);
定义在 prototype 上的方法,必须在实例调用以前进行声明.
用构造函数建立每个实例对象,有些属性和方法都是如出一辙的内容,每一次生成一个实例,都必须为重复的内容.这样会消耗更多内存,也缺少效率.使用 prototype 让共用属性和方法在内存中只生成一次,而后全部实例都指向那个内存地址.
Javascript 规定,每个构造函数都有一个 prototype 属性,指向原型对象.这个对象的全部属性和方法,都会被构造函数的实例继承
Function 构造函数建立一个新的 Function 对象.直接调用此构造函数可用动态建立函数,但会遭遇来自 eval 的安全问题和相对较小的性能问题.然而,与 eval 不一样的是,Function 构造函数只在全局做用域中运行.每一个 JavaScript 函数实际上都是一个 Function 对象 => (function(){}).constructor === Function
...... var singer = Object.create(programmer) ...... console.log(programmer) console.log(singer.__proto__) console.log(singer.__proto__ === programmer)
singer.__proto__ === programmer
获得的结果为 true,实际Object.create()方法建立一个新对象,使用现有的对象来提供新建立的对象的proto.
每一个实例对象都从原型中继承了一个 constructor 属性,该属性指向了用于构造此实例对象的构造函数.
console.log(programmer.constructor) console.log(singer.constructor)
测试都将返回 Person() 构造器,由于该构造器包含这些实例的原始定义.
构造器是一个函数,故能够经过圆括号调用;只需在前面添加 new 关键字,便能将此函数做为构造器使用.
var loser = new programmer.constructor("落魄者",108,"快饿死了") loser.eat() // 落魄者依旧须要吃饭
原型链的经典图:
每一个实例对象( object )都有一个私有属性(称之为 __proto__
)指向它的构造函数的原型对象(prototype ).该原型对象也有一个本身的原型对象( __proto__
) ,层层向上直到一个对象的原型对象为 null.根据定义,null 没有原型,并做为这个原型链中的最后一个环节.
几乎全部 JavaScript 中的对象都是位于原型链顶端的 Object 的实例.
JavaScript 对象有一个指向一个原型对象的链.当试图访问一个对象的属性时,它不只仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾.
遵循 ECMAScript 标准,someObject.[[Prototype]] 符号是用于指向 someObject 的原型.从 ECMAScript 6 开始,[[Prototype]] 能够经过 Object.getPrototypeOf() 和 Object.setPrototypeOf() 访问器来访问.这个等同于 JavaScript 的非标准但许多浏览器实现的属性 __proto__
.
它不该该与构造函数 func 的 prototype 属性相混淆.被构造函数建立的实例对象的 [[prototype]] 指向 func 的 prototype 属性.Object.prototype 属性表示 Object 的原型对象.
function Person(name, age, work) { this.name = name; this.age = age; this.work = work; this.study = function() { console.log(this.name + "学习了" + this.work); }; } Person.prototype.name = "Person"; Person.prototype.type= "地球人"; Person.prototype.eat = function() { console.log(this.name + "依旧须要吃饭"); }; var programmer = new Person("称序员", 42, "coding"); programmer.study(); programmer.eat(); console.log(programmer) console.log(programmer.__proto__) console.log(programmer.constructor.prototype)
根据经典图能够追溯 programmer 的原型
programmer.__proto__
指向 Person.prototypeprogrammer.__proto__.__proto__
指向 Object.prototypeprogrammer.__proto__.__proto__.__proto__
指向 null在实例上有一个属性,在原型上也有属性,优先执行近的,javascript 中的两大链式 => 原型链和做用域链都是'就近原则'
function Person(name, age, work) { this.name = name; this.age = age; this.work = work; this.study = function() { console.log(this.name + "学习了" + this.work); }; } var programmer = new Person("称序员", 42, "coding"); programmer.eat = function(){ console.log("干掉了Person的eat方法") } programmer.type="外星人" Person.prototype.name = "Person"; Person.prototype.type= "地球人"; Person.prototype.eat = function() { console.log(this.name + "依旧须要吃饭"); }; programmer.eat() // 干掉了Person的eat方法 console.log(programmer.type) // 外星人
上面特别更改了赋值的顺序,依旧是执行实例上的方法和属性,这种状况被称为"属性遮蔽 (property shadowing)",java 语言中这就是方法重写.
var o = {a: 1}; // o 这个对象继承了 Object.prototype 上面的全部属性 // o 自身没有名为 hasOwnProperty 的属性 // hasOwnProperty 是 Object.prototype 的属性 // 所以 o 继承了 Object.prototype 的 hasOwnProperty // Object.prototype 的原型为 null // 原型链以下: // o ---> Object.prototype ---> null var a = ["yo", "whadup", "?"]; // 数组都继承于 Array.prototype // (Array.prototype 中包含 indexOf, forEach 等方法) // 原型链以下: // a ---> Array.prototype ---> Object.prototype ---> null function f(){ return 2; } // 函数都继承于 Function.prototype // (Function.prototype 中包含 call, bind等方法) // 原型链以下: // f ---> Function.prototype ---> Object.prototype ---> null
在 JavaScript 中,构造器其实就是一个普通的函数.当使用 new 操做符 来做用这个函数时,它就能够被称为构造方法(构造函数).
function Graph() { this.vertices = []; this.edges = []; } Graph.prototype = { addVertex: function(v){ this.vertices.push(v); } }; var g = new Graph(); // g 是生成的对象,他的自身属性有 'vertices' 和 'edges'. // 在 g 被实例化时,g.[[Prototype]] 指向了 Graph.prototype.
ECMAScript 5 中引入了一个新方法:Object.create().能够调用这个方法来建立一个新对象.新对象的原型就是调用 create 方法时传入的第一个参数:
var a = {a: 1}; // a ---> Object.prototype ---> null var b = Object.create(a); // b ---> a ---> Object.prototype ---> null console.log(b.a); // 1 (继承而来) var c = Object.create(b); // c ---> b ---> a ---> Object.prototype ---> null var d = Object.create(null); // d ---> null console.log(d.hasOwnProperty); // undefined, 由于d没有继承Object.prototype
ECMAScript6 引入了一套新的关键字用来实现 class.使用基于类语言的开发人员会对这些结构感到熟悉,但它们是不一样的.JavaScript 仍然基于原型.这些新的关键字包括 class, constructor,static,extends 和 super.
"use strict"; class Polygon { constructor(height, width) { this.height = height; this.width = width; } } class Square extends Polygon { constructor(sideLength) { super(sideLength, sideLength); } get area() { return this.height * this.width; } set sideLength(newLength) { this.height = newLength; this.width = newLength; } } var square = new Square(2);
在原型链上查找属性比较耗时,对性能有反作用,这在性能要求苛刻的状况下很重要.另外,试图访问不存在的属性时会遍历整个原型链.
遍历对象的属性时,原型链上的每一个可枚举属性都会被枚举出来.要检查对象是否具备本身定义的属性,而不是其原型链上的某个属性,则必须使用全部对象从 Object.prototype 继承的 hasOwnProperty 方法.
hasOwnProperty 和 Object.keys() 是 JavaScript 中处理属性而且不会遍历原型链的方法.
检查属性是否为 undefined 是不可以检查其是否存在的.该属性可能已存在,但其值刚好被设置成了 undefined.
prototype 是用于类的,而 Object.getPrototypeOf() 是用于实例的(instances),二者功能一致.
console.log(typeof x); // undefined console.log(typeof 10); // number console.log(typeof 'abc'); // string console.log(typeof true); // boolean console.log(typeof function () {}); //function console.log(typeof [1, 'a', true]); //object console.log(typeof { a: 10, b: 20 }); //object console.log(typeof null); //object console.log(typeof new Number(10)); //object
判断值类型的用 typeof,判断引用类型的用 instanceof
var obj = { a:10, b:function (){}, c:function (){} }
对象里面一切都是属性,方法也是属性,以键值对的形式表现出来
var func = function () { } func.a = 10; func.b = function () { console.log('hello world'); } func.c = { name:'123', year:1988 }
function Func(){ this.name = 'lili'; this.year = 1988; } var fn1 = new Func();
var obj = {a:20,b:30}; var arr = [1,2,3]; <!--等同于--> var obj = new Object(); obj.a = 20; obj.b = 30; var arr = new Array(); arr[0] = 1; arr[1] = 2; arr[2] = 3;
对象是函数建立的,函数是一种对象
var func = function (){}; console.log(func instanceof Object); // true
//var obj = { a: 10, b: 20 }; //var arr = [5, 'x', true]; <!--以上代码的本质--> var obj = new Object(); obj.a = 10; obj.b = 20; var arr = new Array(); arr[0] = 5; arr[1] = 'x'; arr[2] = true;
console.log(a.prototype.isPrototypeOf(b)); console.log(b.prototype.isPrototypeOf(b));
for(var key in obj) { f(obj.propertyIsEnumerable(key) { <!--do somethings--> }; };
- 判断给定的属性是否能够用 for...in 语句进行枚举同时也是对象的自有属性.
- for ... in 枚举是包含原型链上的属性的,propertyIsEnumerable 做用于原型方法上时,始终是返回 false 的
- for...in 能够枚举对象自己的属性和原型上的属性,而 propertyIsEnumerable 只能判断自己的属性是否能够枚举
- 预约义的属性不是可列举的,而用户定义的属性老是可列举的.因此若是你只想遍历对象自己的属性
function Phone() { } var mi = new Phone(); console.log(mi instanceof Object); //true console.log(mi instanceof Array); //false console.log(mi instanceof Function); //false console.log(mi instanceof Phone); //true // 实例对象的隐式原型链 console.log('实例对象指向Phone.prototype:',mi.__proto__); console.log('Phone.prototype指向Object.prototype:',mi.__proto__.__proto__); console.log('Objec.prototype指向null:',mi.__proto__.__proto__.__proto__); // 构造函数的隐式原型链 console.log('Phone.__proto__指向Function.prototype',Phone.__proto__); console.log('Function.prototype指向Object.prototype',Phone.__proto__.__proto__); console.log('Object.prototype指向null',Phone.__proto__.__proto__.__proto__);
var obj = new Object(); // 通常建立对象的原型链 console.log('建立对象的__proto__',obj.__proto__); console.log('Object.prototype指向null',obj.__proto__.__proto__); function Object(){ } console.log('指向Function.prototype',Object.__proto__); console.log('Object.prototype',Object.__proto__.__proto__); function Function(){ } console.log(Function.__proto__); console.log('Object.prototype',Function.__proto__.__proto__);
上面例子能够知道
graph BT Object.prototype-->|__proto__| null Function.prototype-->|__proto__| Object.prototype Phone.prototype-->|__proto__| Object.prototype mi-->|__proto__| Phone.prototype Phone-->|__proto__| Function.prototype obj-->|__proto__| Object.prototype Function-->|__proto__| Function.prototype Object-->|__proto__| Function.prototype arr-->|__proto__| Array.prototype Array.prototype-->|__proto__| Object.prototype
function Ba(str) { this.name = str ? str : 'baobo'; this.sayHello = function() { alert('hello'); } } Ba.prototype = { alertA:function (){ alert(this.name+'-A'); }, alertB: function() { alert(this.name+''); }, } var instance_b = new Ba('haha'); // constructor console.log('原型的构造函数', Ba.prototype.constructor); //function Object(){} console.log('实例的构造函数', instance_b.constructor); //function Object(){} // 新定义的原型对象,并不具备constructor属性 console.log(Ba.prototype.hasOwnProperty('constructor')); //false
上面的方法至关于重写 Ba.prototype 对象,新定义的原型对象不包含 constructor,所以构造函数指向的 function Object(){},须要显式的给原型添加构造函数
虽然实例对象的 constructor 和构造函数原型的 constructor 都指向构造函数,可是实例对象并不具备 constructor 这个属性,是继承至 Ba.prototype
console.log(ba.hasOwnProperty('constructor')); //false console.log(Ba.prototype.hasOwnProperty('constructor')); //true
graph LR instance_b.constructor-->|实例的构造函数| Ba Ba.prototype.constructor-->|原型对象的构造函数| Ba
Ba.prototype = { constructor: Ba, alertA: function() { alert(this.name + '-A'); }, alertB: function() { alert(this.name + ''); }, } <!--这样就可正确指向构造函数Ba了-->
function Ba(str) { this.name = str ? str : 'baobo'; this.sayHello = function() { alert('hello'); } } Ba.prototype.alertA = function() { alert(this.name + '-A'); } Ba.prototype.alertB = function() { alert(this.name + 'B'); } var instance_b = new Ba('haha'); // constructor console.log('原型的构造函数', Ba.prototype.constructor); //f Ba(){} console.log('实例的构造函数', instance_b.constructor); //f Ba(){}
graph TB 声明构造函数Ba-->构造函数有prototype对象 构造函数有prototype对象-->prototype对象自动有constructor属性 prototype对象自动有constructor属性-->建立实例对象instance_b 建立实例对象instance_b-->继承prototype,有instance_b.constructor 继承prototype,有instance_b.constructor-->instance_b.constructor指向Ba instance_b.constructor指向Ba-->对象有__proto__ 对象有__proto__-->instance_b指向Ba.prototype
function a() { this.name = 'alisy'; } a.prototype.alertA = function () { alert(this.name); } function b() { this.name = 'baobo'; } b.prototype.alertB = function () { alert(this.name); } function c() { this.name = 'cmen' } c.prototype = a.prototype; //b得prototype对象指向一个c的实例,那么全部的b的实例就能继承c b.prototype = new c(); b.prototype.constructor = b; var newb = new b(); var newc = new c(); newb.alertA(); //执行baobo newc.alertA(); //执行cmen //instanceof console.log(b instanceof a); console.log(b instanceof b);
Animal.apply(this, arguments); Animal.call(this, arguments);
Cat.prototype = new Animal(); Cat.prototype.constructor = Cat;
实现继承除了用 call 和 apply 还可使用原型链实现
function add(a+b){ alert(a+b); } function sub(a,b){ alert(a-b); } add.call(sub,3,1); //我的理解call和apply的做用就是切换函数的对象上下文 解:用括号的第一个参数来代替this的指向,将add的执行的上下文由window切换为sub,至关于this指向由window换成sub,add.call(sub,3,1) == add(3,1),结果为alert(4); 注意 : js中的函数是对象,函数名是对Function对象的引用
function Animal(){ this.name = "animal"; this.showName = function(){ alert(this.name); } } function Cat(){ this.name = "cat"; } var animal = new Animal(); var cat = new Cat(); //经过call()和apply(),将本来属于Animal对象的方法showName交给Cat对象使用了,也就是将this指向Animal动态更改成Cat //输出的结果是cat animal.showName.call(cat,"",""); //animal.showName.apply(cat,[]);
function Animal(name) { this.name = name; this.showName = function(name, a, b) { console.log('this是:' + this.name + '\na是:' + a + '\nb是:' + b); } } function Cat(name) { Animal.call(this, name); this.showLog = function() { console.log('hello'); } } Cat.prototype.showAge = function() { console.log('world'); } var cat = new Cat('hello world'); cat.showName('abc', 12, 5); //能够直接调用showName()方法 注意:Animal.call(this);是使用Animal对象代替this对象, this指向Animal,Cat就有了Animal对象中的方法和属性,Cat对 象就能够直接调用Animal对象的方法和属性 call第二个参数开始会映射到Animal相应的参数位置
function Animal() { this.showSub = function(a, b) { console.log(a - b); } } function Cat() { this.showAdd = function(a, b) { console.log(a + b); } } function Dog() { Animal.call(this); Cat.call(this); } var a = new Dog(); a.showSub(5,3);//2 a.showAdd(5,3);//8 使用两个或者更多的call实现多重继承 call和apply这两个方法差很少,区别在于call的第二个参数是任意类型,而apply的第二个参数必须是数组,也能够是arguments
Function.prototype.call2 = function(context) { console.log(arguments); // 若是传入的为null,则指向window context = context || window; // 函数调用的时候,this指向调用的函数 context.fn = this; var args = []; for (var i = 1, len = arguments.length; i < len; i++) { args.push('arguments[' + i + ']'); } // 解析字符串,执行其中的js代码 // 获取返回值 var result = eval('context.fn(' + args + ')'); // 执行完后将添加的属性删除 delete context.fn; return result; }; var heo = 'hello world'; var foo = { name: 'lili' } function func(age, sex) { console.log(age); console.log(sex); console.log(this.name); return { name: this.name, age: age, sex: sex, } } func.call2(null); console.log(func.call2(foo, 23, '男'))
Function.prototype.newApply = function(context, arr) { var result, i, len; context = context || window; context.fn = this; if (!arr) { result = context.fn; } else { var args = []; for (i = 0, len = arr.length; i < len; i++) { args.push('arr[' + i + ']'); } result = eval('context.fn(' + args + ')'); } delete context.fn; return result; } var obj = { name: 'alice' } function func(age, sex) { console.log(age); console.log(sex); console.log(this.name); return { name: this.name } } console.log(func.newApply(obj, [23, '女']));
写程序的一个错误,this 丢失原先的对象,将对象的方法进行赋值以后再执行,因而变成 window.new_showA(),this 指向全局对象 window,
var a = 1; var obj = { a: 11, showA: function () { console.log(this.a); } }; obj.showA(); var new_showA = obj.showA; new_showA(); //1
因此此时须要修改 this 指向
var new_showA = obj.showA.bind(this); new_showA(); //1
实现 bind 方法
Function.prototype.newBind = function (context) { //this是该函数的调用者 var self = this; //arguments.slice(1),从1开始截取数组 var args = Array.prototype.slice.call(arguments, 1); return function () { // 第二个arguments是返回的函数的参数 var bindargs = Array.prototype.slice.call(arguments); return self.apply(context, args.concat(bindargs)); } }; var a = 9; var obj = { a: 99, showA: function (name, age) { console.log(this.a); console.log(name); console.log(age); } }; obj.showA('alice', 12); var new_showA = obj.showA.newBind(obj, 'lilith'); new_showA(23); //至关于执行obj.showA.apply(obj);
function Animal(name, age) { this.name = name; this.age = age; this.voice = 'miao'; } Animal.prototype.type = 'mao'; Animal.prototype.saying = function () { console.log(this.voice); }; var cat = _new(Animal, 'mimi', 10); console.log('instance =>', cat); console.log({ name: cat.name, age: cat.age, type: cat.type }); cat.saying(); function _new() { // 将伪数组arguments从头部删除一个,并将其其返回,此处是为了获取传入的构造函数 var Constructor = Array.prototype.shift.call(arguments); // obj.__proto__指向建立obj的函数的原型,也就是function Object(){}的原型,obj是一个实例对象,没有prototype属性 var obj = Object.create(Constructor.prototype); var result = Constructor.apply(obj, arguments); // 若是构造函数有返回值,作一下处理,若是返回的是对象,就返回对象,不然该是什么就是什么 return typeof result === 'object' ? result : obj; }
<!--Object.create相似于--> function Func(){}; Func.prototype = Constructor.prototype; return new Func();