大前端开发你可能不知道的JavaScript JavaScript

面试 | 你不得不懂得 JS 原型和原型链

2021-03-13  本文已影响0人  林一一呢

推荐阅读地址

掘金

github 求 start😄😄

大家好,我是林一一,这是一篇关于 JS 原型和原型链中的那些事,了解原型和原型链之前需要了解一些 JS 中的OOP,大家也可以直接跳到原型和原型链那里。让我们开始阅读吧。😝

本文思维导图

siweidaotu.png

面向对象编程 OOP (Object Oriented Programming)

JS 中一切都是对象

// 字面量形式
function fn(){}
var obj = {}

// 构造函数模式
var f = new fn()    // ==>   new fn
console.log(f)

var obj = new Object()
console.log(obj)

使用 new 关键字就是利用构造函数创建一个实例,实例就是一个类。另一个就是字面量形式

var num = Object(12)
console.log(num instanceof Number)  // true

var str = Object('12')
console.log(str instanceof String)  // true

var boolean = Object(true)
console.log(boolean instanceof Boolean)  // true
var a = 12
console.log(typeof a)   // 'number'
a.toFixed(2)    // "12.00"

var b = new Number(12)
console.log(typeof b)   // 'object'
b.toFixed(2)    // "12.00"

var c = new String('12')
console.log(typeof c)  // object

需要注意的是基本类型 Symbol() 并支持 new 语法,浏览器不认为 Symbol 是一个构造函数,其他基本类型都可以认为是构造函数。

思考

问:原始值为什么也可以使用属性或方法 a.toFixed ? 不是说原始值就是一个值没有属性嘛?因为使用 new 关键字创建出来的是一个实例,同时字面量形式创建出来的 a 也是一个实例,实例就有属性和方法。字面量形式的创建实际分为三个步骤,以上面代码为示例

var a = 12
a. toFixed(2)

// 相当于下面
/*
* 1. 创建一个 Number 类型的实例
*  var a = new Number(12)
* 2. 调用实例上的方法或属性
*  a.toFixed(2)
* 3. 销毁实例
*/

a.myPro = '12'
console.log(a.myPro)    // undefined

需要注意的是原始值创建的实例,是只读的,所以不能向实例内添加任何属性或方法。如果添加了属性或方法那也是在当前行内创建一个临时的对象,当前行代码运行结束后这对象就已经被销毁了,例如上面的 a.myPro 是 undefined 《红宝书4 P114页》

构造函数的运行机制

function Person(name, age){
    var a = 12
    this.name = name
    this.age = age
}

var person = new Person('林一一', 18)
console.log(person)

思考

1.new Person()这个过程中发生了什么

JS 的变量提升机制

constructorPrint.jpg

上面的 this 指向的就是 Person 这个对象,使用 this 的才会给实例创建属性,var a = 12 就不会给实例创建属性 console.log(person.a) ==> undefined,对比 ES6 中的 class。

2.在构造函数中强制 return 返回值会怎么样?

function Person(name, age){
    var a = 12
    this.name = name
    this.age = age
    return '林一一'
    // return {name: '林一一'}
}

var person = new Person('林一一', 18)
console.log(person)

在构造函数中的 return 会被剥夺,return '林一一' 的返回值还是一个实例 Person {name: "林一一", age: 18}return {name: '林一一'} 的返回值就是 return {name: '林一一'}

原型和原型链

JS 中一切皆对象。基本类型,引用类型都是基于 Object 这个基类创建的。函数也是,prototype 的值也是对象类型。

prototype 和 constructor 和 proto之间的关联

but 过 __proto__ 不是实例的属性,也不是构造函数的属性,在大多数的浏览器中都支持这种非正式的访问方式。实际上 __proto__ 来自 Object.prototype,当使用 obj.__proto__ 时,可以理解成返回了 Object.getPrototypeOf(obj)

<img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1d05ecc2dd954e439683b361d4220849~tplv-k3u1fbpfcp-watermark.image" width="90%" height="350px" />

// 一行代码解析上面的1,2句话
String.prototype.constructor === String   // true

function Fn(){}
var fn = new Fn()

// 对应上面的第三句话
fn.__proto__ === Fn.prototype   // true

//对应上面第四句话
fn.__proto__.__proto__.__proto__ === null   // true

上面代码的成立是因为堆内存中 constructor 的值存储的是函数本身

prototype 原型的作用

 Object.prototype.myName = '林一一'
 var obj = new Object()
 console.log(obj.myName)

原型链机制的查找过程

原型链就是基于 __proto__ 的向上查找机制。当实例操作某个属性或方法时会在当前自己的作用域中查找,找到了则查找结束。没有找到就基于所创建类的原型对象上的 __proto__ 继续向上查找,直到找到基类的 Object.prototype 为止,如果还是没有找到则直接undefined

原型链机制.jpg
图中蓝色的线就是原型链了

思考题,下面结果都输出什么,为什么?

function Fn(){
    var a = 12
    this.getName = function(){
        console.log('private getName')
    }
}

Fn.prototype.getName = function (){
      console.log('public getName')
}

var fn = new Fn()
var fn1 = new Fn()
// 1,2
console.log(fn.a)
console.log(fn.getName())
// 3,4,5
console.log(fn.getName === fn1.getName)
console.log(fn.__proto__.getName === fn1.__proto__.getName)
console.log(fn.__proto__.getName === Fn.prototype.getName)
//6,7
console.log(fn.hasOwnProperty ===Object.prototype.hasOwnProperty)
console.log(fn.constructor === Fn)
/* 输出
*   undefined
*   private getName
*   false
*   true
*   true
*   true
*   true
*/
思考模型题.jpg

解答

1中a 并没有使用 this 是不会写入构造函数内的,输出就是undefined,2中 fn.getName()存在 fn 的私有作用域内输出就是 private getName

3 fn 和 fn1引用堆内存地址不同为false,4中fn 和 fn1 这个实例上的 __proto__指向同一个原型 Fn.prototype 所以为 true。5、同理。

6、fn 中不存在 hasOwnProperty,根据 __proto__向上一级原型Fn.prototype查找也没有,继续根据 __proto__ 向查找到 Object.prototype 找到了 hasOwnProperty,所以输出为true。7同理fn中没有constructor属性,但是会从fn.prototype中查找。

参考

JavaScript深入之从原型到原型链

更多系列的文章已经放在了 github, 欢迎 start 或 issue

感谢阅读到这里,我是林一一,下次见。

上一篇 下一篇

猜你喜欢

热点阅读