本文主题git
// 经过字面量
const obj1 = { name: 'guodada' }
const obj11 = new Object({ name: 'guodada' })
// 经过构造函数
function Pershon() {
this.name = 'guodada'
}
const obj2 = new Pershon()
// Object.create
const obj3 = Object.create({ name: 'guodada' })
复制代码
这里着重点讲原型、构造函数、实例、原型链他们之间的关系,由于这也是面试常问、也是容易混淆的点。github
函数被 new 关键字调用时就是构造函数。面试
new
关键字的内部实现机制(举例说明):浏览器
function Person(name) {
this.name = name
}
const person = new Person('guodada')
复制代码
Person.prototype
;var obj = {} // 建立一个空对象
obj.__proto__ = constructor.prototype //添加 __proto__ 属性,并指向构造函数的 prototype 属性。
constructor.call(this) // 绑定this
return obj
复制代码
(建议看下去再回来看 new 操做符作了什么。。。)函数
每个函数都有一个 prototype 属性。这个属性指向函数的原型对象。post
Person.prototype // {constructor: Pershon(),__proto__: Object}
复制代码
__proto__
那么咱们该怎么表示实例与实例原型 ?学习
每个 JavaScript 对象(除了 null )都具备的一个属性,叫
__proto__
,这个属性会指向该对象的原型ui
person.__proto__ === Person.prototype // true
复制代码
既然实例对象和构造函数均可以指向原型,那么原型是否有属性指向构造函数或者实例呢?this
Person.prototype.constructor === Person
复制代码
总结一下构造函数、实例原型、和实例之间的关系spa
Person.prototype // 构造函数['prototype'] 指向函数原型
person.__proto__ === Person.prototype // 实例['__proto__'] 指向函数原型
Person.prototype.constructor === Person // 函数原型['constructor'] 指向构造函数
复制代码
每个实例都包含一个指向原型对象的
__proto__
指针,依赖这条关系,层层递进,就造成了实例与原型的链条。
function Person() {}
Person.prototype.name = 'Kevin'
var person = new Person()
person.name = 'Daisy'
console.log(person.name) // Daisy
delete person.name
console.log(person.name) // Kevin
复制代码
在这个例子中,咱们给实例对象 person
添加了 name
属性,当咱们打印 person.name
的时候,结果天然为 Daisy
。
可是当咱们删除了 person
的 name
属性时,读取 person.name
,从 person 对象中找不到 name 属性就会从 person
的原型也就是 person.__proto__
,也就是 Person.prototype
中查找,幸运的是咱们找到了 name
属性,结果为 Kevin
。
原型的终点是
null
,由于null
没有proto
属性。
关系图也能够更新为:
顺便还要说一下,图中由相互关联的原型组成的链状结构就是原型链,也就是蓝色的这条线。
js 的基本类型有 String
, Undefined
, Boolean
, Number
, Null
, Symbol
, 咱们通常能够经过 typeof
来判断值的类型
typeof 1 === 'number'
typeof function() {} === 'function'
typeof null === 'object' // 注意!
// 判断引用类型
typeof {} === 'object'
typeof [] === 'object'
复制代码
而引用类型的判断这是经过 instanceof
,用来判断实例是否是另外一个对象的引用.
person instanceof Person // true
复制代码
原理就是: 实例['proto'] === 构造函数['prototype'], 可是值得注意的是 instanceof
会经过原型链继续往下找。
person instanceof Object // true
person.__proto__ === Person.prototype // true
person.__proto__.constructor === Person // true
复制代码
function A() {
B = function() {
console.log(10)
}
return this
}
A.B = function() {
console.log(20)
}
A.prototype.B = function() {
console.log(30)
}
var B = function() {
console.log(40)
}
function B() {
console.log(50)
}
A.B()
B()
A().B()
B()
new A.B()
new A().B()
// 请在浏览器环境下运行
复制代码
上述题目答案是多少呢,你们不妨试试。在看下去(ps 这题还涉及到了执行上下文的概念--考察了函数声明和函数表达式)
答案就在笔者以前写过的文章中 经过一道面试题来学习原型/原型链-函数声明/函数表达式
思考完揭晓答案
A.B()
=> 在 A 原型对象上找到 A.B = function() { console.log(20) }
answer 20B()
=> 同名的函数表达式和函数声明同时存在时 老是执行表达式 answer 40A().B()
A()
执行函数 A ==> 1.变量 B 从新赋值函数 2.返回 this(window).B()
执行全局下的 B 函数 已经被从新赋值 因此输出 10B()
=> 上面的代码执行过 A 函数了,此时全局下的 B 函数输出 10new A.B()
=> new 执行了 A.B = function () {console.log(20)};
new A().B()
new
执行构造函数 A => objA.__proto__ = A.prototype
.B()
在 A 的原型对象中查找 B; A.prototype
指向函数的原型对象A.prototype.B = function () {console.log(30)}
输出 30A.B() // 20
B() // 40
A().B() // 10
B() // 10
new A.B() // 20
new A().B() // 30
复制代码
若有不对之处,请指出~