二十一天,好习惯不一定能养成,但坏习惯肯定能养成
日常白 * 整理
原型
原型-构造器 (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);