原型对象和原型链在前端的工做中虽然不怎么显式的使用到,可是也会隐式的使用了,好比使用的jquery,vue等啦。在进入正题的时候,咱们仍是须要明白什么是__proto__
,prototype
等知识点,主要讲解构造函数,这篇博文大可能是问答形式进行...javascript
原文请戳这里css
也许你会说出工厂模式、构造函数模式、原型模式、组合使用构造函数和原型模式、动态原型模式、寄生构造函数模式和稳妥构造函数
这些,可是咱们能够对他们进行如下归类--属于函数建立对象。html
咱们能够简单的将建立对象的方式分为三种:函数建立对象、字面量建立、Object建立
。固然,也能够只是分红两类:函数建立对象和字面量建立对象
,由于new Object()
中的Object是自己就是一个函数。前端
Object // f Object(){ [native code] }
复制代码
function
(注意是function哦)定义的对象有一个prototype属性,prototype属性又指向了一个prototype对象,注意prototype属性与prototype对象是两个不一样的东西,要注意区别。用伪代码表示以下:vue
var function{
prototype: prototype{} // function的prototype属性指向prototype对象
}
复制代码
注意上面说的是function
里面才会有prototype属性,而咱们new出来的对象里面是没有的哦。java
# function
function Fun(name){
this.name = name;
}
Fun.prototype // {constructor:f}
var fun = new Fun('嘉明');
fun.prototype // undefined
# Object
Object.prototype // {constructor:f,__defineGetter__:f,...}
var object = new Object();
object.prototype // undefined
# 字面量,字面量能够理解没有prototype啦
var jack = {};
jack.prototype // undefined
复制代码
在官方的es5种,定义了一个名叫作[[prototype]]的属性,每一个对象(除了null)都拥有这样一个属性,这个属性是一个指针,它指向一个名叫作原型对象的内存堆。而原型对象也是一个对象,所以又含有本身的[[prototype]]属性,又指向下一个原型对象,终点指向咱们的Object.prototype
对象。jquery
注意⚠️ 这里使用的是[[prototype]],而并不是__proto__。但是他们是同一个东西哈:[[prototype]]是官方所定义的属性,而__proto__是浏览器(就是任性)本身对[[prototype]]所作的实现。编程
分三种状况来讲对象内部的__proto__
:canvas
包含Object()啦
状况一:{}浏览器
var foo = {};
foo.__proto__; // {}
foo.__proto__ === Object.prototype; // true
foo.hasOwnProperty('prototype'); // false 函数才有prototype属性
foo.hasOwnProperty('__proto__'); // false
Object.prototype.hasOwnProperty('__proto__'); // true
复制代码
代码的最后一行,一个是返回了false,另外一个是true。⚠️由于它并不存在于foo对象(foo.__proto__)或者Foo.prototype(Foo.prototype.__proto__)或者Foo(Foo.__proto__)中【下面状况二和三会有代码验证】
,实际上,它是来自于Object.prototype,与其说是一个属性,不如说是一个getter/setter。
状况二:function Foo(){}
1. function Foo(){};
2. Foo.__proto__; // [Function]
3. Foo.__proto__ === Object.prototype; // false
4. Foo.__proto__.__proto__ === Object.prototype; // true
5. Foo.prototype.__proto__ === Object.prototype; // true 函数的原型对象指向
6. Foo.__proto__ == Foo.prototype; //false
7. Foo.hasOwnProperty('__proto__'); // false
8. Foo.hasOwnProperty('prototype'); // true
复制代码
在函数中,经过上面代码2,3,4,5
能够知道Foo.__proto__能够理解为指向了Foo.prototype(广义上理解),而实际上两个又有差异(狭义上,第6点能够说明,欢迎补充)。而后就是每一个函数都有一个默认的prototype
属性,其指向函数的原型对象。
状况三:对象实例 new Foo()
function Foo(){};
var foo = new Foo();
foo.__proto__; // Foo {}
foo.__proto__ === Foo.prototype ; true
foo.hasOwnProperty('prototype'); false
foo.hasOwnProperty('__proto__'); false
复制代码
上面可知,实例中是没有prototype
这个属性的,对比上面的三种状况,也说明了只有在函数中才默认建立了prototype
属性,并且指向了相应的函数原型对象。
在javascript语言中,constructor属性是专门为function而设计的,它存在于每个function的prototype属性中,这个constructor保存了指向function的一个引用。
function F(){
// some code
}
# javascript内部会执行以下的动做
# 1.为该函数添加一个原型(即prototype)属性
# 2.为prototype对象额外添加一个constructor属性,而且该属性保存指向函数F的一个引用
复制代码
对象的实例中也有一个constructor属性(从prototype那里获取的),每个对象实例均可以经过constructor对象访问它的构造函数,以下:
var f = new F();
f.constructor === F; // true
f.constructor === F.prototype.constructor; // true
复制代码
既然能够访问实例的类型f.constructor
,那么咱们就能够对实例进行特殊的处理啦:
if(f.constructor == F){
// some code
}
复制代码
不过别这样操做,由于constructor是不稳定的(见下文对象中的constructor的做用是什么呢?),通常不会采起上面的这种操做,而是经过instanceof
:
if(f instanceof F){
// some code
}
复制代码
这里推荐贺师俊前辈的回答,原文复制以下:
constructor属性不影响任何javascript的内部属性。instanceof检测对象的原型链,一般你是没法修改的(不过某些引擎经过私有的__proto__属性暴露出来)。
constructor其实没有什么用,只是javascript语言设计的历史遗留物。因为constructor属性是能够变动的,因此未必真的指向对象的构造函数,只是一个提示。不过,从编程习惯上,咱们应该尽可能让对象的constructor指向其构造函数,以维持这种习惯。
例子解析:
delete Object.prototype.constructor; // true
({}).constructor; // undefined
({}) instanceof Object; // true
复制代码
《javascript高级程序设计》中有说到全部函数的默认原型都是Object的实例,所以默认原型都会包含一个内部指针,指向Object.prototype。
那么原型的最高指向就是Object了嘛?你能够理解是Object,但是我认为是null。
Object.prototype.__proto__; // null
Object.prototype.__proto__===null ; // true
复制代码
最高指向是Object/null,无伤大雅。
当读取实例的属性时,若是找不到实例的属性,就会查找与对象关联的原型的属性,若是仍是查找不到,就查找原型的原型,一直到顶级为止。
function Person(){
}
Person.prototype.name = "嘉明";
var person = new Person();
person.name = "jiaming";
console.log(person.name); // "jiaming"
// 删除实例的属性后
delete person.name;
console.log(person.name); // "嘉明"
// 追加一个疑问 在__proto__中加属性会覆盖原来的嘛
person.__proto__.name = "嘉";
console.log(person.name); // "嘉" 证实成功,不建议这样修改,毕竟__proto__是浏览器厂商实现的,非标准的
// 再追加一个疑问 __proto__添加的属性或者方法是放在对象的原型上的嘛
var another_person = new Person();
console.log(another_person.name); // "嘉" 证实是放在对象的原型上的
复制代码
属性或者方法在本身的原型上没有找到的话,那就要跑到原型上去找啦。以前有提到过全部函数的默认原型都是Object的实例,所以默认原型都会包含一个内部指针,指向Object.prototype。
那么一个构造函数function Person(){}
就存在这样的一个关系,实例出来的var person = new Person()
person经过__proto__指向构造函数的原型Person.prototype
,而后构造函数的原型指向Object的原型,便是Person.prototype.__proto__
指向Object.prototype
。
嗯,仍是针对构造函数来讲哈,将上面提到的知识点汇总一下啦。上面都是纯文字说明,下面就配上图片好好理解下。
先上相关代码:
function Person(name){
this.name = name;
}
Person.prototype.sayName = function(){
console.log(this.name);
}
var person = new Person("嘉明");
person.age = 12;
person.sayName(); // "嘉明"
console.log(person.name.toString()); // "嘉明"
var another_person = new Person("jiaming");
another_person.sayName();
复制代码
上面代码中,相关的关系以下图
小demo是使用canvas画出小星光,代码以下:
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>canvas</title>
<style> body{ margin: 0; padding: 0; position: relative; } #myCanvas{ position: absolute; left: 50%; top: 50%; background: #000; margin-left: -300px; margin-top: -150px; } </style>
</head>
<body>
<canvas id="myCanvas" width="600" height="300" style="border: 1px solid #000;">
</canvas>
<script src="path/to/canvas.js"></script>
</body>
</html>
复制代码
window.onload = function(){
var c = document.getElementById('myCanvas');
var grd = ""; // 渐变的颜色
// 上下文
var context = c.getContext("2d");
if(context){
// x,y,r 坐标和半径
function Star(x,y,r){
this.x = x;
this.y = y;
this.r = r;
this.init(this.x,this.y,this.r);
}
// 绘制星星
Star.prototype.init = function(x,y,r){
context.beginPath();
// 渐变颜色
grd = context.createRadialGradient(x,y,r-2,x,y,r+2)
grd.addColorStop(0, 'white');
grd.addColorStop(1, 'yellow');
context.fillStyle=grd;
// 画圆
context.arc(x,y,r,0,2*Math.PI);
// 填充颜色
context.fill();
context.closePath();
}
// 建立星星
for(var i = 0; i < 300; i++){
var x = Math.floor(Math.random()*600);
var y = Math.floor(Math.random()*300);
var r = Math.floor(Math.random()*3)+2;
new Star(x,y,r)
}
}else{
var div = document.createElement("div");
div.innerHTML = "您的浏览器不支持canvas,请升级浏览器!";
document.getElementsByTagName("body")[0].appendChild(div);
}
}
复制代码
实现的简单效果以下图哈(ps,您可自行验证哈,改善啥的):
原文请戳这里