首先,咱们来看一个咱们再代码中常常应用的却没有深刻细究的实例

const obj = { key: "value" } obj.toString() // [object Object]

咱们在自定义对象时,仅仅定义 key 的属性,并没有定义 toString 的办法,然而在代码中却能够调用并且取得后果,那么当初我就来深刻的解释一下产生在背地的起因。

必备基础知识

JavaScript 中的数据类型分为两大类,值类型(也叫根本数据类型) 和 援用数据类型

值类型:String、Number、Boolean、null、undefined、Symbol(ES6 新增)
援用数据类型:Object、Function、Array

在这里咱们仅仅来聊一聊 ObjectFunction

// 应用对象间接量创建对象 // 长处: // 1、写法直观,简洁无歧义 // 2、强调对象是一个简略的可变的散列表,而不用肯定派生自某个类 // 3、当应用 Object() 创建对象时,解析器须要顺着原型链开始查找,直到找到 Object 构造函数为止,而间接量的写法令不会 const obj = { key: "value" } console.log(obj)/*  控制台输入后果如下     {key: "value"}         key: "value"         __proto__: Object */

在 JavaScript 中,所有的对象都有 __proto__ 的属性,指向该对象的原型对象,而且 Function 也是对象,是一种非凡的对象

new 一个对象到底产生了什么

轻易百度一下,就能发现常见的,对 new 的过程有如下形容:

  1. 创立一个空对象,将它的援用赋给 this,继承函数的原型;
  2. 通过 this 将属性和办法增加到这个对象上;
  3. 最初返回这个 this 指向的新对象(如果没有手动指定return时);
function myNew (Parent, arguments) {     // 创立一个新对象赋值给this,并 继承函数的原型     // 这一步其实是要拆成两小步的       // 1、创立新对象,赋值给 this           // this = {}       // 2、继承函数的原型           // this.__proto__ = Parent.prototype     // 合并写法     this = Object.create(Parent.prototype)     // 将属性和办法增加到 this 指向的对象上     this.argments = arguments     // 返回这个 this 指向的新对象(如果没有手动指定 return)        return this }

Object.create 创立一个新对象,并应用传入的对象作为该对象的原型

创立(结构)函数

在 JavaScript 中,每一个函数都有一个 prototype 的属性,指向一个对象,该对象即是构造函数的原型对象

每个原型对象都有一个 constructor 属性,指向该原型对象所属的构造函数

无论是应用下列哪种形式创立的函数,都会自带 prototype 属性:

  1. const func = function () {}
  2. const func = new Function () {}
  3. function func () {}

获取原型对象的是3种形式:

1、obj.__proto__,因为 __proto__属性为外部属性,所以个别举荐是应用
2、Parent.prototype,通过父类构造函数的 prototype 拜访
3、Object.getPrototypeOf(obj),通过顶层对象的 getPrototypeOf 办法获取,举荐应用

对象的继承

通过咱们手动的实现 new 运算符能够发现,新创建的对象通过 __proto__ 属性,援用了父类构造函数的原型对象 prototype,由此实现了继承

留神:
1、在 JavaScript 中,每个对象都有 __proto__ 属性,指向该对象的父类构造函数的原型对象
2、在 JavaScript 中,每个函数都有 prototype 属性,即构造函数的原型对象 (因为函数也是对象,所以函数也有 __proto__ 的属性)

原型链

在 JavaScript 中,实现继承的形式,就是通过对象的原型串联起来的,一级一级的向上查找而造成的链式构造,称为原型链

继承

可简略的了解为,即子类无需定义即可应用父类已定义的属性和办法

如何实现继承

/*   1、原型链继承    子类对象继承父类构造函数的实例    长处:既能继承父类原型上的属性和办法,也能继承父类上的属性和办法  毛病:    1、不能动静的给父构造函数传递参数    2、继承繁多    3、所有的子类,都会共享父类实例的属性和办法*/function Parent (name, age) {  this.name = name  this.age = age}const parent = new Parent("obj1", 21)const obj1 = {}obj1.__proto__ = parent/*  2、借用构造函数继承    通过 call / apply 在子类构造函数中动静调用父类构造函数  长处:    1、能够动静的传递参数    2、能够多继承(多个 call/apply)  毛病:    1、只能继承父类的实例属性和办法,不能继承父类原型的属性和办法    2、每个子类构造函数中,都要手动调用父类构造函数,臃肿*/function Parent (name, age) {  this.name = name  this.age = age}function Children (name, age) {  Parent.call(this, name, age)  // ...}const obj2 = new Children("obj2", 22)/*  3、组合继承    组合 原型链继承 和 借用构造函数继承  长处:    1、既能继承构造函数属性,又能继承原型属性    2、能够动静传参  毛病:    1、不反对多继承(父类原型上的属性)    2、子类实例原型上的构造函数指向父类构造函数*/function Parent (name, age) {  this.name = name  this.age = age}function Children(name, age){  Parent.call(this, name, age)  // ...}Children.prototype = new Parent()const obj3 = new Children("obj3", 23)/*  4、原型式继承    通过一个函数封装,创立一个新对象,将传入的对象作为新对象的原型对象,返回新对象  长处:    1、能继承原型上的属性  毛病:    1、不能继承实例上的属性    2、繁多继承*/function Parent (name, age) {  this.name = name  this.age = age}function content(p){  function F(){}  f.prototype = p  return new F() }const obj4 = content(new Parent("obj4", 24))/*  5、寄生式继承    在原型式继承的根底上,再套上一层函数,用于处理函数的传参  长处:    1、反对动静传参  毛病:    1、没用到subject的原型,无奈共享该构造函数原型的属性*/function Parent (name, age) {  this.name = name  this.age = age}function content(p){  function F(){}  F.prototype = p  return new F()}function subject(name, age,level){  const cont = content(new Parent(name, age))  cont.level = level  return cont}const obj5 = subject("obj5", 25, 5)/*  6、寄生式组合继承(举荐)    通过寄生,实现原型上属性的继承    通过组合,实现实例上属性的继承*/function content(obj){  function F(){}  F.prototype = obj  return new F()}// 寄生const cont = content(Parent.prototype)// 组合function Child(name, age){  Parent.call(this, name, age)  // ...}cont.constructor = Child // 惟一的有余Child.prototype = contconst obj6 = new Child("obj6", 26)