什么是JS原型?javascript
若是对JS原型解释的话,会涉及到两个概念:构造函数,原型对象java
通俗的说,就是你在script标签里面声明的那个函数git
<script>
function Test(){
// 我就是构造函数
}
</script>
复制代码
在声明了一个函数以后,浏览器会自动按照必定的规则建立一个对象,这个对象就叫作原型对象。这个原型对象实际上是储存在了内存当中数组
在声明了一个函数后,这个构造函数(声明了的函数)中会有一个属性prototype,这个属性指向的就是这个构造函数(声明了的函数)对应的原型对象;原型对象中有一个属性constructor,这个属性指向的是这个构造函数(声明了的函数)浏览器
以下图:bash
使用构造函数建立对象函数
咱们的构造函数使用new来建立对象的时候,就像下面这样:ui
<script>
function students(){
// 我就是构造函数
}
let stu = new students();
</script>
复制代码
此时,stu就是那个构造函数students建立出来的对象,这个stu对象中是没有prototype属性的,prototype属性只有在构造函数students中有,看图!this
能够看出,构造函数students中有prototype属性,指向的是students对应的原型对象;而stu是构造函数students建立出来的对象,他不存在prototype属性,因此在调用prototype的时候的结构是undefined,但stu有一个__proto__属性,stu调用这个属性能够直接访问到构造函数students的原型对象(也就是说,stu的__proto__属性指向的是构造函数的原型对象),看图:spa
说明:
开始撸代码:
<script type="text/javascript">
function students () {
}
// 可使用students.prototype 直接访问到原型对象
//给students函数的原型对象中添加一个属性 name而且值是 "张三"
students.prototype.name = "张三";
students.prototype.age = 20;
var stu = new students();
/*
访问stu对象的属性name,虽然在stu对象中咱们并无明确的添加属性name,可是
stu的__proto__属性指向的原型中有name属性,因此这个地方能够访问到属性name
就值。
注意:这个时候不能经过stu对象删除name属性,由于只能删除在stu中删除的对象。
*/
alert(stu.name); // 张三
var stu1 = new students();
alert(stu1.name); // 张三 都是从原型中找到的,因此同样。
alert(stu.name === stu1.name); // true
// 因为不能修改原型中的值,则这种方法就直接在stu中添加了一个新的属性name,而后在stu中没法再访问到
//原型中的属性。
stu.name = "李四";
alert(stu.name); //李四
// 因为stu1中没有name属性,则对stu1来讲仍然是访问的原型中的属性。
alert(stu1.name); // 张三
</script>
复制代码
与原型有关的几个方法:
prototype 存在于构造函数中 (其实任意函数中都有,只是否是构造函数的时候prototype咱们不关注而已) ,他指向了这个构造函数的原型对象
constructor属性存在于原型对象中,他指向了构造函数
以下代码:
<script type="text/javascript">
function students () {
}
alert(students.prototype.constructor === students); // true
</script>
复制代码
用构造方法建立一个新的对象以后,这个对象中默认会有一个属性__proto__, 这个属性就指向了构造方法的原型对象
说完原型后,趁热打铁,说一下原型链
在js中,万物皆对象,对象能够说是重中之重了。每个对象都拥有本身的属性。可是在这个世界中有不少东西都是类似的,能够归为一类,他们有共同的方法和属性。不可能让每个对象都定义一个属性吧。那样太消耗内存了。因此,在js中怎么才能让多个对象共享一个或多个方法呢?原型的出现就是为了解决这个问题。
一切皆对象,函数是特殊的对象。
全部的引用类型(函数、数组和对象)都拥有__proto__属性(隐式原型)
全部函数拥有prototype属性(显示原型)
原型对象:拥有prototype属性的对象,在定义函数时就被建立
大多数状况下,__proto__能够理解为“构造器的原型”(__proto__ === constructor.prototype)
function Word(words){
this.words = words
}
Word.prototype = {
alert(){
alert(this.words)
}
}
var w = new Word("Hello Word");
w.alert();
console.log(w.__proto__ === Word.prototype) // true
复制代码
实例w的隐式原型指向它构造函数的显示原型。(指向即恒等于)
w.__proto__ === Word.prototype // true
复制代码
当调用某种方法或查找某种属性时,首先会在自身调用和查找,若是自身没有该属性或方法,则会去它的__proto__属性中调用查找,也就是构造方法的prototype属性中查找。实例经过这样的方法继承构造函数的方法和属性。
实例经过__proto__属性指向构造方法的prototype的属性实现方法和属性的继承。
因为__proto__属性是任何对象都有的属性,在js中一切皆对象,因此会造成一条__proto__链接起来的链条,递归访问__proto__必须最终到头,而且值为null。
Function.prototype.a = 'a';
Object.prototype.b = 'b';
function Person(){}
console.log(Person);
let p = new Person();
console.log(p); // Person {} 对象
console.log(p.a); // null
console.log(p.b); // b
复制代码
说明: p是Person()的实例,是一个Person对象,它拥有一个属性值__proto__,而且__proto__是一个对象,包含两个属性值constructor和__proto__,
会发现p.__proto__===Person.prototype, p.__proto__.constructor ===Person // true,即p.__proto__.constructor指向了构造函数自己。
Person.prototype的__proto__属性,指向了Object 的prototype属性。即p.b的打印结果为b。则实例w经过__proto__属性继承了Object 的属性b。经过__proto__属性一直向上查找,一直到null为止。
那么构造函数Person的__proto__指向了Function.prototype。
事实上,js里彻底依靠"原型链"模式来实现继承。
再简单说一下__proto__、prototype、constructor
继承的实现方式:
为了实现继承,_ _proto _ _会指向上一层的原型对象,而上一层的结构依然相似,那么就利用proto一直指向Object的原型对象上!Object.prototype. _ _ proto _ _ = null;表示到达最顶端。如此造成了原型链继承。
下面有个图很是经典:
你们能够本身动手画一下,加深本身的理解
大体总结一下:
1.Object是做为众多new出来的实例的基类 function Object(){ [ native code ] }
2.Function是做为众多function出来的函数的基类 function Function(){ [ native code ] }
3.构造函数的__proto__(包括Function.prototype和Object.prototype)都指向Function.prototype
4.原型对象的__proto__都指向Object.prototype
5.Object.prototype._ _ proto _ _指向null