构造函数
调用方式
构造函数通过 new 操作符调用,背地里创建一个连接到该函数的 prototype 成员的新对象,同时 this 会被绑定到新对象上,并将该对象作为返回值返回,该对象有自己的 this,通过对象调用构造函数中的方法或属性。
new 的过程
- 隐式的创建了一个新对象(obj),
- 将构造函数的作用域赋值给该对象( this 指向新对象)
- 执行构造函数的代码,将属性方法赋值给该对象,
- 返回新对象。
返回值
- 没有 return 默认返回 新创建的对象。
- 若 return 基础数据类型, 无效,仍然将 this 返回。
- 若 return 引用数据类型,则返回引用数据类型地址。
function Person(name,age){
this.name = name
this.age = age
this.sayName = function(){
console.log('my name is ',this.name)
}
}
// 通过 new 实例化一个 person 对象
const person1 = new Person('yiyi',18)
// 构造函数的函数名与类名相同,即是函数名又是类名
console.log(person1.constructor === Person ) // ====>> true
// 用instanceof 可以检查一个对象是否是一个类的实例
console.log(person1 instanceof Person) // ====>> true
// 所有对象都是Object对象的后代,所以任何对象和Object做instanceof都会返回true
console.log(person1 instanceof Object) // ====>> true
普通函数
调用方式
可直接调用,由 window 调用,没有自己的 this
返回值:
- 没有return 默认return undefined
- 有 return 则返回函数体中 return 的内容
function person {
console.log('yiyi')
}
// 直接调用,this 指向 window
person()
小结
函数在定义时是无法区分是否为构造函数, 只有在调用时才可以区分出来
任何函数只要通过 new 操作符调用的就可作为构造函数
构造函数首字母大写,普通函数采用驼峰命名
原型
每个对象都有
__proto__
属性,但只有函数对象才有 prototype 属性
prototype(原型对象)
每个函数都有一个 prototype 属性,该属性为一个指针,指向原型对象,该对象用于存放所有实例共有的属性和方法。
person1.__proto__ === Person.prototype
// 返回对象的原型
Object.getPrototypeOf(person1) === Person.prototype
constrcutor
所有的原型对象都会自动获得一个 constructor(构造函数)属性,这个属性(是一个指针)指向 prototype 属性所在的构造函数(Person)。
每个对象都能找到其对应的构造函数,这个 constructor 可能是对象自己本身显式定义的或者通过__proto__
在原型链中找到的。
通过函数 new 创建的实例对象即使自己没有constructor 属性,它也能通过__proto__
找到对应的 prototype上的 constructor,所以任何对象最终都可以找到其构造函数
Person.prototype.constructor === Person
// person1 本身并不具备 constructor 属性,通过原型链继承而来
person1.constructor === Person
person1.hasOwnProperty('constructor') // false
person1.constructor === Person.prototype.constructor
__proto__
每个实例化对象中都有一个隐藏的 **
__proto__
**属性,该属性指 向构造函数的原型对象(即父类对象)。实例与构造函数之间没有直接的关系,通过__proto__
与原型对象创建关系
虽然实例中不包含属性和方法, 但通过原型链的查找实现了该功能
原型链 ( __proto__与prototype)
每个对象通过__proto__
属性指向它的原型对象,这个原型对象又有自己的原型,直到某个对象的原型为 null 为止,这种一级一级的链结构就称为原型链。
- 原型就是我们的prototype
- 链就是
__proto__
,它让整个链路连接起来。
显示原型
利用函数上的prototype属性查找原型。
隐式原型
利用对象上的
__proto__
属性查找原型,这个属性指向当前对象的构造函数的原型对象,
__proto__这个属性是对象上的属性,所以可以在实例对象上面使用。
当读取实例的属性时,如果找不到,就会查找与对象关联的原型中的属性,如果还查不到,就去找原型的原型,一直找到最顶层Object为止。
Object是JS中所有对象数据类型的基类(最顶层的类),Object.prototype._ proto_ === null
由上图可知存在三个构造函数 Person()、 Function()、 Object()
函数皆由 Function() 构造而来,Function()本身也是函数,所以Function()也是自己的实例
所以函数的 __proto__
指向了 函数的原型Function.prototype。
构造函数实例化的对象上存在__proto__
属性并指向 Object.prototype。
Person.prototype = function.prototype
function.prototype.__proto__ = Object.prototype
object.prototype.__proto__ =null // object 原型的尽头是null
Person() 构造函数是由函数而来函数的 prototype 指向 function.prototype (函数的原型)
所以由此得出一下结论:
// 原型对象也是对象,所以它也有原型即 Object.prototype
Person.prototype.__proto__ === Object.prototype
- Person 函数是属于 Function 这个构造函数的,由Function 构造而来Person 的,为 Function() 的实例对象
- 构造函数作为实例对象时
__proto__
指向 Function 的原型(Function.prototype) - Function的原型本身为一个对象,对象的
__proto__
指向原型:Object.prototype。 - Object() 作为构造函数时本身也是一个函数,
相应地,构造函数 Object() 的 prototype 属 性指向原型对象Object.prototype;
实例对象 Person.prototype 的 __proto__
属性一样指向原型对象 Object.prototype。
除了使用.__proto__
方式访问对象的原型,还能够经过 Object.getPrototypeOf 方法来获取对象的原型 ,以及经过 Object.setPrototypeOf 方法来重写对象的原型。
我们可以使用对象的 .__proto__
来检查对象自身中是否含有该属性;使用in检查对象中是否含有某个属性时,如果对象中没有但是原型中有,也会返回true
原型的判断方法
获取对象原型
除了使用 .proto 方式访问对象的原型,还能通过 Object.getPrototypeOf 方法来获取对象的原型 ,以及 Object.setPrototypeOf 方法来重写对象的原型。
hasOwnProperty()
我们可以使用对象的 hasOwnProperty() 来检查对象自身中是否含有该属性,hasOwnProperty()方法存在于原型对象中的__proto__中。
in()
使用 in 检查对象中是否含有某个属性时,会沿着原型链查找,如果对象中没有但是原型中有,也会返回true。
instanceof
我们常用 typeof 来判断数据的类型,但是typeof 只能用于判断基础数据类型,当判断引用数据类型的时无论什么类型的变量,都会返回 Object。
所以这里引入了 instanceof 来判断引用数据类型。
instanceof 也可以用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
通过判断参数对象的原型链上是否能找到构造函数的 prototype,来确定 instanceof 的返回值。
实现 instanceof 方法,有三种:
- Object.getPrototypeOf(obj)——返回指定对象的原型(内部 [[Prototype]] 属性的值)
- Object.prototype.isPrototypeOf(obj)——测试一个对象是否存在于另一个对象的原型上
- obj.proto——使用非标准的 proto 的伪属性
- 使用 Object.getPrototypeOf(obj)
function instance_of(o, c) {
let op = Object.getPrototypeOf(o);
const cp = c.prototype;
while(true) {
if (!op) {
return false
}
if (op === cp) {
return true
}
op = Object.getPrototypeOf(op);
}
}
- Object.prototype.isPrototypeOf(obj)
function instance_of(o, c) {
return c.prototype.isPrototypeOf(o);
}
- obj.proto
function instance_of(o, c) {
let op = o.__proto__;
const cp = c.prototype;
while(true) {
if (op === null) {
return false
}
if (op === cp) {
return true
}
op = op.__proto__;
}
}
万物起源 null
一切皆为对象,却不知,JavaScript的世界中的对象,追根溯源来自于一个 null。
null 也是做为一个对象存在,基于它继承的子子孙孙,当属对象。乍一看,null 像是上帝,而 Object 和 Function 犹如JavaScript世界中的亚当与夏娃
顺着原型链查找: person1_ —> _Person.prototype —> Object.prototype —> null
Object.prototype 的原型指向null,由 null 作为原型链的终点,
null 在某种意义上来说也是一种对象,为表示一个为“空”的对象。
person1.__proto__ === Person.prototype
person1.__proto__.constructor === Person
Person.prototype.__proto__ === Object.prototype // 原型对象的原型链 指向 原型对象
person1.__proto__.__proto__=== Object.prototype
Person.__proto__ === Function.prototype // 函数对象的原型指向函数的原型对象
Function._ proto_ === Function.prototype // Function的原型指向函数原型对象
Object._ proto_ === Function.prototype //Object的原型指向函数原型对象
Function.prototype._ proto_ === Object.prototype
//Function的原型对象的原型指向Object的原型对象
person1.__proto__ === Person.prototype //person1的原型指向构造函数Person的原型对象
Person.prototype._ proto_ === Object.prototype //Person的原型对象的原型指向Object的原型对象
Object.prototype._ proto_ === null //Object的原型对象的原型指向null【原型链顶层】
Object 、Function
Function 对象比较特殊,它的构造函数就是它自己
Function 既可以看成是一个函数,也可以是一个对象,所有函数和对象最终都是由 Function 构造函数得来,任何函数均可以看作是经过 Function() 构造函数的new实例化的结果。
// Function类本身也是Function的一个实例
Function.prototype === Function.__proto__
// 它是它自己的构造函数
Function === Function.constructor
//Function 的本质是个对象 函数的prototype属性,自己是一个由Object构造的实例对象
Function.prototype.__proto__ === Object.prototype
// 函数和对象最终都是由 Function 构造函数得来
Object.__proto__ === Function.prototype
// Object 本身是Function 的一个实例
以下四者绝对相等
- Funtion.prototype (Function的
__proto__
指向其构造函数Function的prototype) - Function.
__proto__
( Function() 本身是由Function构造而来 ) - Object.
__proto__
(Object() 函数对象,本身是Function 的实例,由Function构造而来) - Person.
__proto__
( Person() 构造函数本身是函数 是 Function 的一个实例)
Object.__proto__ === Function.prototype
Function.__proto__ === Function.prototype
若是把函数Person当成实例对象的话,其构造函数是Function(),其原型对象是Function.prototype;相似地,函数Object的构造函数也是Function(),其原型对象是Function.prototype。
Object作为一个构造函数(是一个函数对象),所以他的
__proto__
指向Function.prototype;
Function.prototype.__proto__ === Object.prototype
而 Function.prototype (原型对象)本质为一个对象,对象的__proto__
为 Object.prototype,
所有原型对象的原型链最终都指向Object.prototype,而Object.prototype的prototype指向null(尽头);
先有鸡还是先有蛋
Function.prototype.__proto__ === Object.prototype
, 可知 Function.prototype 是一个 Object 实例,那么应当是先有 Object 再有 Function。
可是Object.__proto__ === Function.prototype
。 这样看来,没有 Function,Object也不能建立实例。
这就产生了一种「先有鸡仍是先有蛋」的经典问题,究竟是先有的 Object 还是先有的 Function 呢?
自盘古开天辟地,js中并不是就有了Object,而是Object.prototype。
js中的万物(原始值除外)都是继承自Object,唯独一个对象例外,那就是Object.prototype。
Object instanceof Object; //true
Object.prototype instanceof Object; // false
全局下的 Object 构造自 Function.prototype,而 Function.prototype 构造自 Object.prototype。
Object.getPrototypeOf(Object) === Function.prototype
Object.getPrototypeOf(Function.prototype) === Object.prototype
所以,是先有的Object.prototype,再有的 Function.prototype,再有的 Function 和 Object。
产生步骤:
- Object.prototype先于Object出现
- 然后用这个 prototype 构造 Function.prototype
- 有了 Function.prototype 再构造出 Function、Object 这几个构造器
- 然后把 Object.prototype 挂到 Object 上,Function.prototype 挂到 Function上
伪代码大致是这样,create元操作的含义是使用给定的对象作为原型构造一个新的对象。
var ObjectPrototype = create( ); // 开天辟地
var FunctionPrototype = create( ObjectPrototype );
//FunctionPrototype(后被赋值给了Function.prototype)是Object类型的
//因为其原型是ObjectPrototype
var Function = create( FunctionPrototype );
Function.prototype = FunctionPrototype;
// Function是Function类型的,也是Object类型的
//言外之意,Function对象 原型链上有Function.prototype和Object.prototype
Object = create( FunctionPrototype );
Object.prototype = ObjectPrototype;
//Object是Function类型的,也是Object类型的
//言外之意Object对象的原型链上有Function.prototype和Object.prototype
Function对象、Object对象的原型链上有Function.prototype和Object.prototype
总结
-
prototype 属性是函数所独有的;
__proto__
和 constructor 属性是对象所独有的。
函数js中的一等公民,也是一种对象,所以函数也拥有__proto__
和 constructor 属性。 -
原型对象 prototype用于存放实例公有属性和方法,原型对象都是Object()的实例。constructor 指向对象的构造函数。
-
原型链又叫隐式原型链,是由
__proto__
属性串联起来,原型链的尽头是 Object.prototype.proto==>null
-
构造函数是使用了new关键字的函数,用来创建对象,所有函数都是Function()的实例
所以函数(此时看成了对象)的构造函数都指向 Function。
-
Object 构造自 Function.prototype,而 Function.prototype 构造自Object.prototype
参考文章
- 【一张图理解js原型】http://www.javashuo.com/article/p-vpjbjgnw-cd.html
- 【javascript原型等概念】http://www.javashuo.com/article/p-wpvyqmmr-ck.html
- 【js中先有Function,还是先有Object】https://zhuanlan.zhihu.com/p/44724367