ES6前没有引入类的概念,使用构造函数与原型模拟类的实现。
创建对象的三种方式
使用
new Object()
创建对象let obj = new Object();
利用对象字面量创建对象
let obj = {};
利用构造函数创建对象
function People(name, age) { this.name = name; this.age = age; this.say = function() { console.log('永远滴神'); } } let uzi = new People('乌兹', 18); uzi.say();
构造函数
构造函数用来初始化对象,即为对象成员变量赋初始值,总是与new一起使用
new在执行时做的四件事
- 在内存中创建一个新的空对象
- 让this指向这个新的对象
- 执行构造函数中的代码,给这个新的对象添加属性和方法
- 返回这个新对象(所有构造函数中不需要return)
静态成员和实例成员
- 静态成员:在构造函数本身上添加的成员
- 实例成员:构造函数内部通过this添加的成员,只能通过实例化的对象来访问
function People(name, age) {
this.name = name;
this.age = age;
this.say = function() {
console.log('永远滴神');
}
}
// name,age,say为实例成员
let uzi = new People('乌兹', 18);
uzi.say();
// 静态成员,只能通过构造函数访问
People.sex = "男"
console.log(People.sex); // 男
console.log(uzi.sex); // undefined
原型
构造函数原型对象prototype
构造函数存在浪费内存的问题,每new一个对象就会开辟新的内存空间存放参数和方法
使用原型来解决这个问题
JavaScript规定,每个构造函数都有一个prototype属性,指向另一个对象,这个prototype就是一个对象,这个对象的所有属性和方法都会被构造函数所拥有,构造函数通过原型分配的函数是所有对象所共享的
可以把那些不变的方法,直接定义在prototype对象上,这样所有对象的实例就可以共享这些方法
// 一般情况下,公共属性定义到构造函数里,公共方法放到原型对象上
function People(name, age) {
this.name = name;
this.age = age;
}
People.prototype.say = function() {
console.log('永远滴神')
// 这里的this也指向实例对象
}
let uzi = new People("wuzi", 18);
let theShy = new People("shy", 12);
uzi.say === theShy.say; // true, 内存位置相同
对象原型__proto__
对象都会有一个属性__proto__
指向构造函数的prototype原型对象,之所以对象可以使用构造函数prototype原型对象的属性和方法,就是因为对象有__proto__
原型的存在
uzi.__proto__ = People.prototype
原型constructor
对象原型和原型对象中都有一个constructor属性,用于记录对象引用哪个构造函数
很多情况下,手动的利用constructor属性指回原来的构造函数
function People(name, age) {
this.name = name;
this.age = age;
}
People.prototype = {
// 如果我们修改了原来的原型对象,给原型对象赋值的是一个对象,则必须手动的利用constructor指回原来的构造函数
constructor: People,
say: function() {
console.log('111');
}
run: function() {
console.log('222');
}
}
原型链
只要是对象就有__proto__
原型,Object原型对象中的__proto__
指向null,为最上层
方法查找规则:首先看对象本身是否有say方法,如果有就执行该方法,如果没有,因为存在__proto__
,就去构造函数原型对象prototype中查找该方法,通过原型链一层层向上查找。
拓展内置对象方法
Array.prototype.sum = function() {
let sum = 0
for(let i = 0; i < this.length; i++) {
sum+=this[i];
}
return sum;
}
let arr = [1,2,3];
console.log(arr.sum()); // 6
继承
ES6前没有提供extends属性提供继承功能,通过构造函数+原型对象模拟继承
// 借用父构造函数继承属性
function Father(name, age) {
// this 指向父构造函数的对象实例
this.name = name;
this.age = age;
}
Father.prototype.money = function() {
console.log(10000);
}
function Son(name, age, sex) {
// this 指向子构造函数的对象实例
Father.call(this, name, age);
this.sex = sex;
}
// Son.prototype = Father.prototype; 这样指向同一个地址,修改子原型对象后,父原型对象也会被修改
Son.prototype = new Father();
// 利用对象覆盖原有的原型对象后,要将constructor指回原来的构造函数
Son.prototype.constructor = Son;
Son.prototype.exam = new function() {
console.log("exam");
}
let son = new Son('wuzi', 18, '男');
实现New方法
function myNew() {
// 创建一个空对象
const obj = {};
// 获取第一个参数,即构造方法
Constructor = Array.prototype.shift.call(arguments);
// 将空对象的对象原型指向构造方法的原型对象
obj.__proto__ = Constructor.prototype;
// 使用apply改变构造函数的指向,并传入参数
Constructor.apply(obj, arguments);
// 返回该对象
return obj;
}
const Tom = myNew(People, 'Tom', 18);
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!