原型与原型链

二十一天,好习惯不一定能养成,但坏习惯肯定能养成

日常白 * 整理

原型
原型-构造器 (constructor)

原型是跟对象密不可分。如果我们需要创建一个对象,就需要先去定义一个object。一般情况,用关键字var申明。比如有一个对象叫张三,年龄20

var zhangsan = {
 name:'张三',
 age:20
}

如果有李四,就继续创建一个李四的对象。当然,如果要创建100个这样的对象,哪怕ctrl+c/v ,程序员也怕烦,也懒。所以,我们会想到用函数function来动态创建

function User(name, age) {
    var person = {} // 定义一个person 对象
    person.name = name; // 往对象中绑定传参
    person.age = age;
    return person // 返回生成的新对象
}

var zhangsan = User('张三', 20);
var lisi = User('李四', 22);

但是我们通常是用构造器来声明创建

function User(name, age) {
  this.name = name; // 这里面的this,就代表了即将生成的那个对象 ,并且绑定传参
  this.age = age;
}

var zhangsan = new User('张三', 20);
var lisi = new User('李四', 22);

需要用 new 关键字来创建对象的函数,称之为:“构造器 constructor or 构造函数”,生成对象的这个过程,称之为:实例化。“zhangsan” 可以称之为一个对象,也可以称之为一个 实例。

原型-proto & prototype

创建对象 在对象中添加一个功能属性,可以引用自己的属性 “greet”

function User(name, age) {
  this.name = name; // 这里面的this,就代表了即将生成的那个对象 ,并且绑定传参
  this.age = age;
  this.greet = function () {
    console.log('你好, 我是' + this.name + ',我' + this.age + '岁');
  }
}

var zhangsan = new User('张三', 20);
var lisi = new User('李四', 22);

zhangsan.greet() // 你好我是张三,我20岁
lisi.greet() // 你好我是李四,我22岁

我们比较两个实例化的greet

zhangsan.greet() === lisi.greet()  // false

张三 和 李四,实例化之后,都在自己的内部,创造了 greet 这样的属性。
这个时候,greet 的功能都是一模一样的呀。如果实例100个对象,岂不是要拷100份?完全没必要呀。有没有什么方法将这些通用的属性,放到一个地方呢?

所以,重头戏之一 prototype。先看一个普通对象

function test1 () {}
console.log( test1.prototype ) // { constructor : f }

function test2 () {}
console.log( test2.prototype ) // { constructor : f }

每创建一个function,都会自带一个 prototype 这样的对象。这就是js 的原生机制。那为什么 js 的原生机制 要这么做呢?划重点:prototype 就是给他即将生成的对象,继承下去的属性

function User(name, age) {
  this.name = name; // 这里面的this,就代表了即将生成的那个对象(实例对象) ,并且绑定传参
  this.age = age;
}
User.prototype.greet = function () {
  console.log('你好, 我是' + this.name + ',我' + this.age + '岁');
}

var zhangsan = new User('张三', 20);
var lisi = new User('李四', 22);

zhangsan.greet() === lisi.greet()  // true

所以实例了两个对象,但是是继承了同一个greet对象

我们打印其中李四

User {name: "李四", age: 22}
age: 22
name: "李四"
__proto__:
greet: ƒ ()
constructor: ƒ User(name, age)
__proto__: Object

通过 prototype 定义的greet 属性跑到__ proto__ 下面去了。并且,这个greet属性虽然没有在自己本身的对象下面,但是一样可以使用。那__proto__到底是什么

function Test () {}
Test.prototype.name = 'test'
var test01 = new Test()
var test02 = new Test()
test01.__proto__ === test02.__proto__    // true
// ----------------------- 实例之后的对象调用__proto__指针指向的 等于被实例的构造函数的prototype!
// test01.__proto__ = Test.prototype  // true

原来通过prototype 定义的属性,再被多个实例化之后,引用的地址是同一个!并且 proto 就是我们上面使用的prototype 属性的别名啊!就是说,我们在构造函数中使用prototype 定义的属性,都会被 proto 指针引用!

结论: 每个对象都有__proto__属性,指向创建该对象的构造函数的原型 ,然后通过__proto__属性将对象链接起来,组成一个原型链,用来实现继承和共享属性。

那如果在原型上定义了属性,更改之后,是否实例化相应的更改吗。事实上是可以更改的

function Test () {}
Test.prototype.name = 'test'
var test01 = new Test()
console.log( test01.name ) // "test"
Test.prototype.name = 'no test '
console.log( test01.name ) // "no test"

再回过来看看constructor

function User(name, age) {
  this.name = name; 
  this.age = age;
}
User.prototype.greet = function () {
  console.log('你好, 我是' + this.name + ',我' + this.age + '岁');
}
var lisi = new User('李四', 22);

// 再次构造
var zhangsan = new lisi.constructor('张三', 20) // 使用constructor来实例化!!!
new lisi.constructor() === new User()  // true
console.log(zhangsan)
/*
  User {}
  name:'张三'
  age = 20
  __proto__
  greet:f()
  constructor : f User (name, age)
  __proto__:Object
  ...
  */  

哪怕我们只知道实例后的对象,但是我可以通过 proto 去找到这个实例对象的构造函数 constructor ,我再通过这个构造函数再去实例对象。

原型-原生对象的原型
var a ={}
console.log(a)
/*{}
__proto__:
constructor: ƒ Object()
...
*/

可以看到,我们var 了一个新对象之后,没有定义任何属性,但是也能看到他的构造函数:Object()。也就是说:var a ={} === var a = new Object(),两者没有任何区别

  var a ={}
  var b = new Object()
  console.log(a.constructor === b.constructor ) // true

可以看到,构造函数完全一样。
那么这个时候,可能会有同学想问,怎么去创造一个干净的对象呢?里面没有任何集成的属性等。

 var a = Object.create({name:"张三"}) // 创建函数必须传参,一个对象或者是 null ,否则会报错!
  console.log( a )
  /*
    {}
    __proto__
    name:"张三"
      __proto__
      constructor : f Object()
      ...
    */ 

可以看到,再Object.create() 中传入对象的属性,是放在第一层的 proto 下面的,也就是中,这是你创建的这个原型对象的继承属性,意味着,可以根据自身的业务需求,来定义自己的原型对象!

多级继承链

从动物到哺乳 到人类 到张三李四

// Animal --> Mammal --> Person --> me
// Animal 
function Animal(color, weight) {
  this.color = color;
  this.weight = weight;
}
Animal.prototype.eat = function () {
  console.log('吃饭');
}

Animal.prototype.sleep = function () {
  console.log('睡觉');
}
 //  Mammal
function Mammal(color, weight) {
  Animal.call(this, color, weight); // 改变this指向
}

Mammal.prototype = Object.create(Animal.prototype);
Mammal.prototype.constructor = Mammal;
Mammal.prototype.type= function () {
  console.log('哺乳');
}
//  Person
function Person(color, weight) {
  Mammal.call(this, color, weight); // 改变this指向
}

Person.prototype = Object.create(Mammal.prototype);
Person.prototype.constructor = Person;
Person.prototype.lie = function () {
  console.log('说谎');
}
// 实例
var zhangsan = new Person('brown', 100);
var lisi = new Person('brown', 80);
console.log('zhangsan:', zhangsan);
console.log('lisi:', lisi);  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值