一、前奏:面向对象
创建对象的方式:
- 简写方式,直接通过语法糖简写
var a = {} - 通过new Object创建
let a = new Object()
关于Object:
可以看出Object是一个函数,之所以可以用new Object创建对象是因为,Object的本质是一个构造函数,构造函数和普通的函数没有本质区别,只是可以用来构造一些东西。
二、对象和构造函数
写一个构造函数:
- 内部用this指代需要生成的实例化对象
- 使用new实例化对象
- 函数的名称需要首字母大写
function Foo(a) {
this.a = a;
this.b = a;
this.say = function () {
console.log("哈哈哈")
}
}
var obj = new Foo(3);
obj;//Foo {a: 3, b: 3, say: ƒ}
obj.say();//哈哈哈
构造函数和实例化对象的关系是什么?
——构造函数是妈,构造函数实例化的对象是儿子。
因为JavaScript是没有类的。只有基于原型。在构造函数中通过new关键字可以生成对象,和基于类的语言非常类似,所以我们称构造函数也是一个类。
instanceof用于判断一个变量是否是某个对象的实例
obj instanceof Foo;// true
Object是对象,同时也是构造函数。
Object instanceof Object;//true
Object instanceof Function;//true
任何对象都可以看作是Object的实例。
三、constructor构造器
任何(实例化)对象拥有一个属性constructor,构造器。它指向当前对象的构造函数。
function Person(a) {
this.name = a;
}
var p = new Person("张三");
如果没有修改Person的原型,p.constructor===Person为true
如果修改了Person的原型,p.constructor是Object,不是Person
Person.prototype = {
say: function () {
//通过this访问实例化对象的属性
console.log(`我的名字是${this.name}`)
}
}
// 这一步的本质是通过new Object(say())创建一个对象赋值给Person.prototype
// 所以Person.prototype.constructor === Object
var p2 = new Person("李四");
p2.say()// 我的名字是李四
p2.constructor === Person // false
// 修正constructor的指向,修正之后访问constructor就可以知道实例化对象的构造函数
Person.prototype.constructor = Person
p2.constructor === Person // true
函数也是一个对象,也有构造函数
Person.constructor === Function;//true
Function.constructor === Function;//true
Object.constructor === Function;//true
可得:
Function构造了Person构造函数
Function构造了Function
Function构造了Object
四、原型(构造函数的基因)
只有构造函数拥有原型 prototype,可以直接访问修改,相当于函数的基因,可以让实例化对象使用的公用的方法。
let proto = {
say: function () {
console.log('say')
},
walk: function () {
console.log('walk',this === obj2)
}
}
Foo.prototype = proto;
var obj2 = new Foo(5);
console.log(obj2)// Foo {a: 5, b: 5, say: ƒ}
// 实例化对象的基因(proto 隐式原型)
obj2.__proto__ ; // {say: ƒ, walk: ƒ}
obj2.walk(); // "walk,true"
console.log(obj2.__proto__ === Foo.prototype)// true
console.log(obj.__proto__ === obj2.__proto__)// true
// 构造函数的原型就是实例化对象的隐式原型
obj2通过 __proto__ 继承继承了母亲(Foo)的原型(prototype),于是,我们就把实例化对象和构造函数关联到一起了。通过 __proto__ 可以访问到构造函数原型的方法。
function Foo(){
this.name = "Foo构造函数"
}
Foo.prototype.say = function(){
console.log(this.name);
}
let fo1 = new Foo();
fo1.say();//函数存在并执行,访问了fo1构造函数原型上的方法
new关键字做了什么??
function Foo(){}
//let o = new Foo();等价于做了以下
o = new Foo();//新建一个对象,此时o.__proto__ === Object.prototype
o.__proto__ = Foo.prototype;//修改对象原型指向
Foo.call(o);//将this指向o执行
隐式转化原理:
原理: {}调用了隐式原型__proto__即Object.prototype中的toString()方法转换成"[object Object]"
原理: 根据原型链,调用原型的时候,对象里面有toString()方法优先调用对象里面的
Function,所有函数的构造函数。
let fo1 = () => {};
fo1.__proto__ === Function.prototype;//true
let fo2 = function(){};
fo2.__proto__ === Function.prototype;//true
function fo3(){}
fo3.__proto__ === Function.prototype;//true
访问对象的构造函数 constructor
let o = {};
o.constructor === Object;// true
任意函数都是由Function构造来的。 Object是构造函数,所以
Object.__proto__ === Function.prototype;//true
//甚至
Function.__proto__ === Function.prototype;//我构造了我自己!
任意对象都是由Object构造出来的。
let o1={};
o1.__proto__ === Object.prototype;//true
构造函数Object的原型对象的构造器是他自己
Object.__proto__ === Function.prototype;//true
Object.prototype.constructor === Object;//true
Function.prototype.constructor === Function
Function是由Function构建出来的,Object是由Function构建出来的。Object的原型对象是由自己构建出来的
五、原型链
每一个对象都有一个原型对象,对象以原型为模板,继承原型的方法和属性。原型也可以拥有原型,也继承方法和属性,一层一层,这个关系就叫原型链。
首先这个JavaScript的世界里面有个Function,是上层语言设计出来的。上层语言的Function生成了一个Object并且将Object的原型修改成了自己。
Object.__proto__===Function.prototype;//true
然后Object生成了Function的原型。
Function.prototype.__proto__===Object.prototype;//true
那么先有鸡还是先有蛋?Object是由Function构造出来的
观察:
Function.__proto__ === Function.prototype;//Function自己创造了自己
Function.constructor === Function;
Function.prototype.__proto__ === Object.prototype;//Function的原型的构造函数是Object
Object.__proto__ === Function.prototype;//Object是由Function构造的
Object.prototype.constructor === Object;//原型的构造函数是他自己!
Object.prototype.__proto__ === null;//万物之源!
于是得出以下上帝造人的故事。
- Object.prototype是凭空出来的(是上层对象创建的),Object.prototype的构造函数指向了自己。
- 根据Object.prototype生成了Function构造函数的原型
- Function就由此产生,并且它自己的构造函数就是自己
- Function生成了Object构造函数。
- Object生成了万物,包括构建了(window对象和document)
对象身上属性的访问顺序:
先在对象身上查找,如果没有就查找隐式原型,如果还是没有就去隐式原型里面的隐式原型查找,直到找到Object的原型。这个查询顺序就是原型链,这也就是为什么每个对象都有toString方法的原因了。因为Object.prototype上有这个方法。
Object.prototype对象
拓展:
生成一个对象,函数内部的this会变成这个对象。
var obj = Object.create()//创建一个新的对象,接收一个参数
var obj2 = Object.create(null) //{} 无中生有,不适用于Object原型链的规则
obj2.__proto__ //undefined create创建的对象没有任何参数方法
obj2.__proto__ = Person.prototype //关联原型
Person.call(obj2)//通过call关联this
obj2.say()//报错
obj2.__proto__.say() //__proto__是属性不是原型
var obj3 = Object.create({});//除了null,传入其他的内容都适用原型链查找规则
obj3.__proto__ // 有参数