浅谈JS中的New和prototype

浅谈JS中的New和prototype

new运算的具体执行过程

  • 创建一个空对象
  • 把这个空对象的__proto__指向构造函数的prototype
  • 把这个空对象赋值给this
  • 执行构造函数内的代码

例1[牛客网:4399]

var A={n:4399};
var B=function () {
    this.n=9999;};
var C=function () {var n=8888;
    };
B.prototype=A;
C.prototype=A;
var b=new B();
var c=new C();
A.n++;
console.log(b.n);
console.log(c.n);


解法

访问b.n,存在n属性(值),直接输出b.n,b.n=9999。
访问c.n,不存在n属性(值),通过原型链__proto__向上寻找,c.__proto__指向C.prototype也就是A,所以就是输出A.n

例2[贝壳笔试前端18届]

10.对于下面的代码,控制台中输出结果顺序正确的是 (D) 
考点:this的指向, new
	var name = ‘abc’;
	var person = {
		name: ‘cba’,
		getName: function () {
			return this.name;
}
};
console.log(person.getName());//调用person的getName函数,this指向person中的name
var p1 = person.getName;
console.log(p1());//只是调用getName函数,此时this指向window
var p2 = new p1();
console.log(p2.name)//p1无name属性(值)

A.cba,,cba,abc
B.cba,abc,abc
C.cba,cba,cba
D.cba,abc,undefind

构造函数、原型与实例之间的关系

每创建一个函数,该函数就会自动带有一个 prototype 属性。该属性是个指针,指向了一个对象,我们称之为 原型对象。什么是指针?指针就好比学生的学号,原型对象则是那个学生。我们通过学号找到唯一的那个学生。假设突然,指针设置 null, 学号重置空了,不要慌,对象还存在,学生也没消失。只是不好找了。
  原型对象上默认有一个属性 constructor,该属性也是一个指针,指向其相关联的构造函数。
  通过调用构造函数产生的实例,都有一个内部属性,指向了原型对象。所以实例能够访问原型对象上的所有属性和方法。
  
  所以三者的关系是,每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。通俗点说就是,实例通过内部指针可以访问到原型对象,原型对象通过constructor指针,又可以找到构造函数。

示例1

	function Dog (name) {
    this.name = name;
    this.type = 'Dog'; 
}
Dog.prototype.speak = function () {
  alert('wang');
}
var doggie = new Dog('jiwawa');
doggie.speak();  //wang 

以上代码定义了一个构造函数 Dog(), Dog.prototype 指向的原型对象,其自带的属性construtor又指回了 Dog,即 Dog.prototype.constructor == Dog. 实例doggie由于其内部指针指向了该原型对象,所以可以访问到 speak方法。
 浅谈JS中的New和prototype_第1张图片

示例2

Dog.prototype 只是一个指针,指向的是原型对象,但是这个原型对象并不特别,它也只是一个普通对象。假设说,这时候,我们让 Dog.protptype 不再指向最初的原型对象,而是另一个类 (Animal)的实例
浅谈JS中的New和prototype_第2张图片

//定义一个Animal构造函数,作为Dog的父类
function Animal() {
    this.superType='Animal';
}
Animal.prototype.superSpeak=function () {
    alert(this.superType);
    
}
function Dog(name) {
    this.name=name;
    this.type='Dog';
}
//改变Dog的prototype指针,指向一个Animal实例
Dog.prototype=new Animal();
Dog.prototype.speak=function () {
    alert(this.type);
}
var doggie=new Dog('jiwawa');
doggie.superSpeak();//Animal

解释

以上代码,首先定义了一个 Animal 构造函数,通过new Animal()得到实例,会包含一个实例属性 superType 和一个原型属性 superSpeak。另外又定义了一个Dog构造函数。然后情况发生变化,代码中加粗那一行,将Dog的原型对象覆盖成了 animal 实例。当 doggie 去访问superSpeak属性时,js会先在doggie的实例属性中查找,发现找不到,然后,js就会去doggie 的原型对象上去找,doggie的原型对象已经被我们改成了一个animal实例,那就是去animal实例上去找。先找animal的实例属性,发现还是没有 superSpeack, 最后去 animal 的原型对象上去找才找到。

浅谈JS中的New和prototype_第3张图片

Appendix

JS的继承方式

在js中,继承就是让一个对象拥有另一个对象的属性和方法。下面就来谈谈怎样实现继承。

(Father代表父类构造函数,Son代表子类构造函数,默认构造函数的方法都是写在原型中,实例化对象共享原型中的方法,避免了内存空间的浪费)

1,原型链继承 (有两种实现方式)

(1)Son.prototype = Father.prototype

弊端:Son.prototype.constructor 指向Father,需要手动更改为Son ;Son的实例化对象只能继承Father原型中的方法,无法继承Father本身的属性。

(2)Son.prototype = new Father()

弊端:Son.prototype.constructor 指向Father,需要手动更改为Son;Son的实例化对象共享Father自身的引用类型属性。什么意思呢?下面举个栗子:

function Father(){ this.name = “zs” ; this.arr = [1,2,3] }

function Son(){ }

Son.prototype = new Father()

var s1 = new Son(), s2 = new Son();

s1.arr.push(5);

console.log(s1.arr)--------> [1,2,3,5]

console.log(s2.arr)--------->[1,2,3,5]

看了这里例子就明白了,Son的实例化对象s1,s2继承了Father的属性arr,但是s1,s2是同时指向这一属性的。

2,借助构造函数继承

function Father(){ this.name = “zs”; this.age=38 };

function Son(){ Father.call( this) / Father.apply(this) }

弊端:Son只能继承Father自身的属性,而无法继承Father原型中的方法。

3,组合式继承

将原型链继承与构造函数结合起来

function Father(){ this.name=“zs”;this.age=38 }

Father.prototype.sayHi = function(){ alert(“hello”) }

function Son(){ Father.call(this) }

Son.prototype = new Father()

var s = new Son() ;

弊端:通过Father.call() 和 new Father() ,父类构造函数Father被调用了两次。

4,原型式继承

function createObj(o){ function F(){ } F.prototype=o ; return new F() }

var obj = { name:“zs” , age:18, sayHi:function(){ } }

var newObj = createObj( obj );

newObj继承了obj的属性和方法,但是同样出现了共享父类中引用类型属性的问题。

5,经典继承(Es5中的新语法:存在兼容性问题,需要做浏览器的能力检测)

function create(obj){

if(Object.create){ return Object.create(obj) }

else { function F(){ } F.prototype=o ; return new F() }

}

6,寄生式继承(类似于原型式继承)

function createObj(o){ function F(){ } F.prototype=o ; return new F() }

function createObj2(o){ var obj = createObj(o) ; obj.sayHi = function(){ } return obj }

var obj = { name:“zs” , age:18, sayHi:function(){ } }

var newObj = createObj2(obj)

newObj继承了obj的属性和方法,但是同样出现了共享父类中引用类型属性的问题。

7,寄生组合式继承(组合继承+寄生继承)

function createObj(o){ function F(){ } F.prototype=o ; return new F() }

function inheritPrototype(Child, Father) {

var prototype = object(Father.prototype);//创建对象

prototype.constructor = Child;//增强对象

Child.prototype = prototype;//指定对象 }

function Father(name) {

this.name = name;

this.arr = [1, 2, 3, 4]; }

Father.prototype.sayName = function () { console.log(“父类原型” + this.name); }

function Child(name, age) { Father.call(this, name); this.age = age; }

inheritPrototype(Child, Father)

Child.prototype.sayAge = function () { console.log(this.age); }

var child1 = new Child() , child2 = new Child();

child1.arr.push(5) ------> [1,2,3,4,5]

child2.arr ------> [1,2,3,4].

优点:可以多重继承 解决两次调用 解决实例共享引用类型的问题 原型链保持不变

你可能感兴趣的:(前端)