关于面试:面试-你不得不懂得-JS-原型和原型链

8次阅读

共计 4435 个字符,预计需要花费 12 分钟才能阅读完成。

举荐浏览地址:

掘金

大家好,我是林一一,这是一篇对于 JS 原型和原型链中的那些事,理解原型和原型链之前须要理解一些 JS 中的 OOP,大家也能够间接跳到原型和原型链那里。让咱们开始浏览吧。????

本文思维导图

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

  • 面向过程:__就是剖析出解决问题所须要的步骤,而后用函数把这些步骤一步一步实现,应用的时候一个一个顺次调用就能够了。__
  • 面向对象:__是把形成问题事务分解成各个对象,建设对象的目标不是为了实现一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。__
  • 面向过程长处:性能比面向对象高,因为类调用时须要实例化,开销比拟大,比拟耗费资源; 比方单片机、嵌入式开发、Linux/Unix 等个别采纳 面向过程开发,性能是最重要的因素。毛病:没有面向对象易保护、易复用、易扩大
  • 面向对象长处:易保护、易复用、易扩大,因为面向对象有封装、继承、多态性的个性,能够设计出低耦合的零碎,使零碎 更加灵便、更加易于保护 毛病:性能比面向过程低

JS 中一切都是对象

  • 每一个数据类型底层的封装都是基于 Object 来创立的,援用类型 Function() 函数 Array() Date() Math(),String(), Number(), 等根本类型,nodeLIst 节点汇合,window 等都是
  • 基于构造函数 constructor 创立自定义类
// 字面量模式
function fn(){}
var obj = {}

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

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

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

  • Object 是一个工厂办法能依据传入的类型,转换成相应的原始包装类型
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 代码执行之前,构造函数会在以后公有作用域内创立一个对象也就是开拓一个堆内存空间,但临时不存储任何内容。浏览器会让函数中的主体 this 指向这个堆内存地址
  • 代码自上而下执行
  • 最初代码执行完结后,浏览器会把创立的对像堆内存的对象默认返回,不须要写 return。返回的也就是一个实例

JS 的变量晋升机制

下面的 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: '林一一'}

  • 在构造函数中 return 的返回值是原始值时,浏览器返回的还是实例
  • 强制返回一个创建对象时,返回的就是创立的对象。不合乎咱们想要失去一个类的实例。所以在构造函数中应用 return 是没有多大意义的

原型和原型链

JS 中所有皆对象。根本类型,援用类型都是基于 Object 这个基类创立的。函数也是,prototype 的值也是对象类型。

prototype 和 constructor 和 __proto__之间的关联

  • 每一个函数类型都自带一个 prototype 的原型属性,原型是对象类型,浏览器会开拓一个堆内存空间。
  • 浏览器会给这个堆内存空间中增加一个 constructor 的属性,属性值是构造函数自身。构造函数中并没有 constructor 属性,然而会从构造函数的 prototype 中查找,obj.constructor===obj.prototype.constructor
  • 每一个对象都有一个 __proto__ 的属性,这个属性指向所创立类的 prototypeprototype 也是对象同样也有 __proto__这个属性。函数是对象吗?是的,所以函数也有 __proto__这个属性。如果不能确定指定的类,那 __proto__ 会指向 Object。
  • object 这个基类的 __proto__ 指向的是本人自身,__proto__ 最终指向值是 null

but 过 __proto__ 不是实例的属性,也不是构造函数的属性,在大多数的浏览器中都反对这种非正式的拜访形式。实际上 __proto__ 来自 Object.prototype,当应用 obj.__proto__ 时,能够了解成返回了 Object.getPrototypeOf(obj)

// 一行代码解析下面的 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 原型的作用

  • 每一个类都会把公共的属性和办法存储到原型上,给实例调用。
  • 给所创立类的原型 prototype 增加属性和办法就是给实例增加共有办法。
 Object.prototype.myName = '林一一'
 var obj = new Object()
 console.log(obj.myName)

原型链机制的查找过程

原型链就是基于 __proto__ 的向上查找机制。当实例操作某个属性或办法时会在以后本人的作用域中查找,找到了则查找完结。没有找到就基于所创立类的原型对象上的 __proto__ 持续向上查找,直到找到基类的 Object.prototype 为止,如果还是没有找到则间接undefined


图中蓝色的线就是原型链了

思考题,上面后果都输入什么,为什么?

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
*/

解答

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

感激浏览到这里,我是林一一,下次见。

正文完
 0