原型编程的基本规则:
- 所有的数据都是对象
- 要得到一个对象,不是通过实例化类,而是找到一个对象作为原型并克隆它
- 对象会记住它的原型
- 如果对象无法相应某个请求,它会把这个请求委托给它自己的原型
直接上图
一、继续说说构造函数
- 构造函数只有一个特点:函数名是大驼峰式
- 构造函数必须用
new
才能生产一个对象 - 构造函数不停的调用,可以生产出多个相似且独立的对象
- 生成对象这个过程,称之为 实例化对象
二、原型链属性的增删改
- 增
通过对象修改原型的东西不太现实,因为后代不可能修改祖先的东西
只有通过
Person.prototype.xxx
增加东西到原型上
Person.prototype.name = '张三';
function Person() {}var person = new Person();
- 删
delete Person.prototype.name; // true 删除成功
- 改
Person.prototype.name = '李四'; //=> 只能通过这种方式修改原型上的东西
三、原型上的 constructor
属性
constructor
会返回构造这个对象的构造函数
function Person() {}var person = new Person();
console.log(person.constructor); //=> Person()
constructor
可以手动更改
Person.prototype = {constructor: Car // 更改原构造函数的 constructor
}function Person() {}function Car() {}var person = new Person();
- 用
constructor
再次对象实例化
function Person(name, age) {this.name = name;this.age = age;
}var person = new Person('mary', 18);
var person1 = new person.constructor('smith', 20); // 再次使用 construcor 来实例化
console.log(person.constructor === person1.constructor); // true
- 通过把实例对象赋值给原型对象 来延长原型链
const Person = function (name, age) {this.name = name;this.age = age;
};Person.prototype.sayName = function () {return this.name;
}const Student = function () {
};Student.prototype = new Person(); // 把 Person 的实例对象 赋值给 Student 的原型对象
Student.prototype.constructor = Student; // 把 构造者 改为 Studentconst me = new Student();
结果
此时的原型链有四层了,普通对象只有三层
- 构造函数里面的
this
function Person() {// var this = {// // }// 这里面的 this, 指向即将实例化的那个对象console.log(this); // Person {...}
}var person = new Person();
四、__ proto __
属性
__ proto __
干啥的???
Person.prototype.name = '张三';
function Person() {// var this = {// __proto__ : Person.prototype// };// __proto__ 指向的是对象的原型// 原对象里面是没有 name 属性的
}var person = new Person();
console.log(person.name); // 张三 --> 在原型上找到了 name 属性
这就是调用一个对象的属性,如果对象属性里面没有的话,就会上原型里面找的原因。
- 更改
__ proto __
指向
Person.prototype.name = '李四';
function Person() {// 原来的 person.__proto__ 指向的是原型对象
}var obj = {name : '张三'
};var person = new Person();
person.__proto__ = obj; // __proto__ 属性是可以更改的
console.log(person.name); // 张三
- 原型链的连接点
__ proto __
Grand.prototype.name = '张三';
function Grand() {}var grand = new Grand();Father.prototype = grand;
function Father() {}var father = new Father();Son.prototype = father;
function Son() = {}var son = new Son();
console.log(son.name); // 张三
// 但是如果 Grand 上面都没有的话就会上原型链的终端 Object.prototype 上面去找,如果还是没有就会打印 null。!!!
总结: 每一个对象都有一个
__proto__
属性, 指向该对象的原型. 实例后通过对__proto__
属性的访问 去对prototype
对象进行访问; 原型链是由原型对象组成的, 每一个对象都有__proto__
属性, 指向创建该对象的构造函数的原型, 然后通过__proto__
属性链接起来, 组成一个原型链, 用来实现继承和共享属性!
五、Object.create(prototype)
方法
- 还可以用
Object.create(prototype)
方法创建对象
var obj = {name : '张三',age : 18
}var obj1 = Object.create(obj);
- 模拟构造函数
Person.prototype.name = '张三';
function Person() {}var person = Object.create(Person.prototype);
- 判断题:全部对象最终都会继承自
Object.prototype
var obj = Object.create(null); // 构造出一个空的对象,里面啥都没有
// 所以是错的,应该是绝大多数对象最终都会继承 Object.prototype
这就是为什么 undefined.toString() / null.toString() 会报错的原因了
undefined / null 没有原型,也不会经过包装类
- 123.toString() 也会报错
因为 JS 会认为 123 后面有 . 点,就是一个浮点型,而浮点型后面是不能跟字母的
- JS
重写
var num = 123;
// num.toString(); // new Number(num).toString(); --> Number(); 原型上面有 toString() 方法;Number.prototype.toString = function() {// 虽然原型上有这个方法,但是我有重新写了一个不同功能的 toString() 方法console.log('that change is true');
}// Number 的原型指向 Object.prototype
// NUmber.prototype.__proto__ = Object.prototype// 包括 Object.prototype.toString() 也可以更改
Object.prototype.toString = function() {console.log('that change is true too');
}
原型上有,但我自身写了一个和原型上那个方法同名,但是不同功能的方法
Object.prototype.toString = function() {return 'i am win'; //=> 主要还是看 return 的返回值
}
Person.prototype = {// toString : function() { // 把系统自带的 toString() 方法覆盖了// return 'hello world';// }
}
function Person() {}var person = new Person();
person.toString(); // i am win
- 有
toString()
方法的
object.prototype.toString();
Number.prototype.toString();
String.prototype.toString();
Boolean.prototype.toString();
六、 探讨 document.write()
方法
var obj = {};document.write(obj); // [object Object]
// 但实际上是 toString() 的结果
document.write(obj.toString()); // [object Object]
// 说明网页面输出数据,都会调用 toString() 方法// 再次证明
var obj = Object.create(null); // 让原型、方法那些都清空
document.write(obj); // 报错var obj = Object.create(null);
obj.toString = function() { // 再次检测 document.write() 是不是调用了 toString() 方法return 'hello world';
}document.write(obj); // hello world