> 上来先说定义:JavaScript 是一种基于原型而不是基于类的基于对象(object-based)语言。
这句话其实就说出了javascript面向对象的本质,javascript中许多比较难以理解的东西比如 原型链,闭包
都跟javascript面向对象相关,因此与传统语言比如java或php的面向对象进行比较,就能很好的理解javascript面向对象的本质。
再看知乎大神的说法:
> 面向对象本质上就是函数作用域的一种封装。函数的闭包和对象原型链,就可以实现类似java的面向对象,闭包和原型链语法更加底层,也就是说更加强大和难理解。
那对于比较熟悉面向对象的同学来说,通过对比与java等面向对象的异同就能很快理解javascript的面向对象实现思路
- 先简单看一下继承实现方法一,通过Manager.prototype = new Employee()实现
//可以在Employee构造函数中添加属性方法,这样子类对象可以继承
//也可以在Employee构造函数外通过原型prototype添加属性和方法
//那这有什么不同?
//其实没什么不同,在Employee构造函数外其实是更加灵活,所以javascript是动态语言
//当然一般都会把方法放到原型链上,所有实例可以共享,不然每个实例一个方法对象有点浪费
function Employee(name, dept) {
this.name = name || "";
this.dept = dept || "general";
this.showInfo = function () {};
}
Employee.prototype.specialVal = "Employee都有份";
Employee.prototype.specialfunction = function () {};
function Manager(name, dept, reports) {
this.reports = reports || [];
}
//通过这种方法把原型链连接起来
Manager.prototype = new Employee('dealdot','marketing')
//Employee.prototype.specialVal = "Manager都有份";
var m1 = new Manager('zlw', 'marketing',['re1','re2'])
//手动指定一下,不然Manager的constructor有问题
Manager.prototype.constructor = Manager
//首先在Manager构造函数里找没找到
//再去Manager的prototype里找 Manager.prototype 没找到
//因为 Manager.prototype--> new Employee,去Employee构造函数里找,没找到
//再去 Employee.prototype里找,找到了
//现在如果再找一个叫toString()的方法,还会继承往链上找,即查找Employee.prototype.__proto__
console.log(Manager.prototype.constructor === Manager) //false
console.log(m1.__proto__.__proto__.__proto__ === Object.prototype) //true
- 在Manager构造函数里 Employee.call(this, name, dept) 实现直接继承,缺点:原型链上的不能继承
function Employee(name, dept) {
this.name = name || "";
this.dept = dept || "general";
this.showInfo = function () {console.log('showInfo')};
}
Employee.prototype.specialVal = "Employee都有份";
Employee.prototype.specialfunction = function () {console.log('specialfunction')};
function Manager(name, dept, reports) {
//通过这种方法实现继承,这种方式只能继承Employee构造函数的属性,不能继承到原型链上的
Employee.call(this, name, dept);
this.reports = reports || [];
}
//Manager.prototype.specialVal = "Manager都有份";
var m1 = new Manager('dealdot', 'marketing', ['re1','re2'])
console.log(m1.specialVal)
console.log(m1.__proto__.__proto__ === Object.prototype) //true
- 在第二点基础上用 Employee.call() 继承构造函数的方法 + Object.create(Employee.prototype)实现原型链继承
function Employee(name, dept) {
this.name = name || "";
this.dept = dept || "general";
this.showInfo = function () {console.log('showInfo')};
}
Employee.prototype.specialVal = "Employee都有份";
Employee.prototype.specialfunction = function () {console.log('specialfunction')};
function Manager(name, dept, reports) {
//通过这种方法实现继承,这种方式只能继承Employee构造函数的属性,不能继承到原型链上的
Employee.call(this, name, dept);
this.reports = reports || [];
}
//Manager.prototype.specialVal = "Manager都有份";
//通过Object.create() 实现原型链接继承
Manager.prototype = Object.create(Employee.prototype)
//或者直接用下边这种
// Manager.prototype = Employee.prototype
//手动指定一下,不然Manager 的 constructor有问题
Manager.prototype.constructor = Manager
var m1 = new Manager('dealdot', 'marketing', ['re1','re2'])
console.log(m1.specialVal)
console.log(m1.__proto__.__proto__.__proto__ === Object.prototype)
在 JavaScript 中,您可以在运行时为任何对象添加属性,而不必受限于构造函数提供的属性。--动态语言的灵活性
那么方法1和方法3比较有什么不同呢,可以打印对象看下区别
方法1打印结果
方法3打印结果
可以看到方法一更符合继承逻辑,即Manager实例只有自己的属性,而方法三则是把Employee的属性直接添加到Manager中了,但是第三种方法给父构造函数传参更方便,综合考虑js继承还是推荐方法三
- 下边再看代码:
//可以在Employee构造函数中添加属性方法,这样子类对象可以继承
//也可以在Employee构造函数外通过原型prototype添加属性和方法
//那这有什么不同?
//其实没什么不同,在Employee构造函数外其实是更加灵活,所以javascript是动态语言
// 当然一般都会把方法放到原型链上,所有实例可以共享
function Employee(name, dept) {
this.name = name || "";
this.dept = dept || "general";
this.showInfo = function () {};
}
Employee.prototype.specialVal = "Employee都有份";
Employee.prototype.specialfunction = function () {};
function Manager(reports) {
//取到Employee的属性,后边都是一样的套路
Employee.call(this);
this.reports = reports || [];
}
//把Manager对象的原型链加到Employee的原型链上
//Manager.prototype = Object.create(Employee.prototype)
Manager.prototype = new Employee();
Manager.prototype.specialValUse = "Manager都有份";
function WorkBee(name, dept, projects) {
//通过指定base属性实现继承也可以,但用call与apply最好
// this.base = Employee
// this.base(name, dept)
Employee.call(this, name, dept);
this.projects = projects || [];
}
//WorkBee.prototype = Object.create(Employee.prototype)
WorkBee.prototype = new Employee();
WorkBee.prototype.specialValUseWorkBee = "WorkBee都有份";
function SalesPerson(quota) {
WorkBee.call(this);
this.dept = "sales";
this.quota = quota || "";
}
//SalesPerson.prototype = Object.create(WorkBee.prototype)
SalesPerson.prototype = new WorkBee();
// function Engineer(machine) {
// WorkBee.call(this)
// this.dept = 'engineering'
// this.machine = machine || ''
// }
function Engineer(name, dept, projects, machine) {
// this.base = WorkBee
// this.base(name, dept, projects)
//WorkBee.call(this, name, dept, projects)
WorkBee.apply(this, [name, dept, projects]);
//machine属性是本地值,其它则是继承值
this.machine = machine || "";
}
//Engineer.prototype = Object.create(WorkBee.prototype)
Engineer.prototype = new WorkBee();
var jane = new Engineer(
"dealdot",
"市场部",
["project1", "project2"],
"GK machine"
);
jane.bounds = 300;
// jane.projects = ["navigator"];
console.log(jane);
//console.log(jane.__proto__.__proto__.__proto__ === Employee.prototype)
//构造函数对应prototype,实例对象对应__proto__ 都可以查看出原型链
console.log(jane.__proto__ === Engineer.prototype);
console.log(jane.__proto__.__proto__.__proto__.__proto__.__proto__);
- 看分析图
- 实际打印对象分析
本文由 dealdot <dealdot#163.com> 创作, Full Stack Developer @ DeepBlue
本文最后编辑时间为: Apr 19, 2021 at 13:54 pm
转载请注明来源