Q:JavaScript面向对象与Java面向对象系统的区别
-
原型 是JavaScript面向对象系统的根本,类 是JAVA面向对象系统的根本。
原型(Prototype)模式是一种设计模式,同时也是一种编程范式(programming paradigm)。
类是对一类实体的结构、行为的抽象。在基于类的面向对象语言中,首先关注的是 抽象 ,需要先把具体通用性的类设计出来,才能用这个类去实例化一个对象,进而关注到具体层面的东西。 - 在JS这样的原型语言中,首先关注的是 具体 ,具体的每个实例的行为。根据不同实例的行为特性,把相似的实例关联到一个原型对象里去。在这个被关联的原型对象里,囊括了那些较为通用的行为和属性,基于此原型的实例,都能“复制”它的能力。
在原型编程范式中,我们正是通过 “复制” 来创建新对象。但这个 “复制” 未必一定要开辟新的内存、把原型对象照着再实现一遍 —— 我们复制的是能力,而不必是实体。比如在 JS 中,我们就是 通过使新对象保持对原型对象的引用来做到了 “复制”。
ES6中的类
ECMAScript 2015 中引入的 JavaScript 类实质上是 JavaScript 现有的基于原型的继承的语法糖。类语法不会为 JavaScript 引入新的面向对象的继承模型。 ——MDN
当我们尝试用class
去定义一个Dog类时,完全等价于写一个构造函数:
class Dog {
constructor(name ,age) {
this.name = name
this.age = age
}
eat() {
console.log('肉骨头真好吃')
}
}
function Dog(name, age) {
this.name = name
this.age = age
}
Dog.prototype.eat = function() {
console.log('肉骨头真好吃')
}
原型与原型链
原型编程范式的核心思想就是 利用实例来描述对象,用实例作为定义对象和继承的基础。在 JavaScript 中,原型编程范式的体现就是 基于原型链的继承。
构造函数constructor
构造函数,在对象创建或实例化时被调用的方法。通常使用该方法来初始化数据成员和所需资源。构造器constructor不能被继承,因此不能重写,能被重载。对象的constructor属性返回创建该对象的函数的引用。
构造函数与普通函数的区别
- 调用方式不同.
任何函数,只要通过new操作符调用,那它就可以作为构造函数.否则为普通函数
var person = new Person("s", 2);
person.sayName(); // 当作构造函数使用
Person("s", 2); // 添加到window这
window.sayName(); // 当作普通函数使用
Q:用 new 去创建一个实例时,new 做了什么?
- 为这个新的对象开辟一块属于它的内存空间
- 把函数体内的 this 指到 1 中开辟的内存空间去
- 将新对象的 _ proto_ 这个属性指向对应构造函数的 prototype 属性,把实例和原型对象关联起来
- 执行函数体内的逻辑,最后即便你没有手动 return,构造函数也会帮你把创建的这个新对象 return 出来
第二步执行完之后,实例对象的原型就把构造函数的 prototype 的引用给存下来了。
var A = function() {};
A.prototype.n = 1;
var b = new A();
A.prototype = {
n: 2,
m: 3
}
var c = new A();
console.log(b.n);
console.log(b.m);
console.log(c.n);
console.log(c.m);
后面对 A 的 prototype 做了修改啊!b 如果存的是引用,它应该感知到我这个修改啊,为什么输出的不是2,3,2,3
,而是1,undefined,2,3
呢?注意你修改 A 的 prototype 的形式
A.prototype = {
n: 2,
m: 3
}
这严格意义上来说不算修改,而是一个重新赋值操作。这个动作的本质是把 A 的 prototype 指向了一个全新的 js 对象:
A 单方面切断了和旧 prototype 的关系,而 b 却仍然保留着旧 prototype 的引用。这就是造成 b 实例和 c 实例之间表现差异的原因。
原型
在 JavaScript 中,每个构造函数(如Person)都拥有一个 prototype 属性,它指向构造函数的原型对象(Person.prototype),这个原型对象中有一个 construtor 属性指回构造函数;
每个实例(person)都有一个__proto__
属性,当我们使用构造函数去创建实例时,实例的__proto__
属性就会指向构造函数的原型对象。
person.__proto__ === Person.prototype // true
Person === Person.prototype.constructor // true
原型也是一个对象,既然是对象,我们就可以用最原始的方式创建它:new Object()
。
javascript中的原型(prototype)是和函数(function紧密连接的),prototype是函数才会有的属性。每个对象都有__proto__
属性来标识自己所继承的原型,但是只有函数才有prototype属性。
Q: 什么是原型?
A:每一个JavaScript对象(null除外)在创建的时候就会与之关联另一个对象,这个对象就是我们所说的原型,每一个对象都会从原型"继承"属性。
继承是一个十分具有迷惑性的说法,引用《你不知道的JavaScript》中的话,就是:继承意味着复制操作,然而JavaScript默认并不会复制对象的属性,相反,JavaScript只是在两个对象之间创建一个关联,这样,一个对象就可以通过委托访问另一个对象的属性和函数,所以与其叫继承,委托的说法反而更准确些。
Q:var o = {} 不是用function 他有原型吗?
答:必须的.ES规范定义对象字面量的原型就是Object.prototype.即o.__proto__ == Object.prototype.
每一个通过new操作符生成出来的对象都持有一个属性__proto__
,这个属性保存了创建他的构造函数的prototype的原型的引用.
每个对象都有__proto__
属性来标识自己所继承的原型,但是只有函数才有prototype属性
> function test(){} // 函数
> var a = new test();
> a.__proto__ == test.prototype;
true
> test.__proto__ == Function.prototype;
true
// > test.__proto__ == Object.prototype;
false
> test.prototype.__proto__ == Object.prototype;
true
> test.prototype.constructor == test;
true
> a.__proto__.constructor == test;
true
> test.prototype
test {}
> a instanceof test
true // a继承test
> a instanceof Object
true //a 也继承Object
> a.__proto__
test {} // a的__proto__属性为test.prototype 被覆盖毕竟是属性的值
> var a = {name: 'bubble'} //对象
> a.name
'bubble'
> a.__proto__ == Object.prototype;
true
> a.__proto__ == {name: 'bubble'}.prototype;
false
> {name: 'bubble'}.prototype
... // 不存在
原型链
// 创建一个Dog构造函数
function Dog(name, age) {
this.name = name
this.age = age
}
Dog.prototype.eat = function() {
console.log('肉骨头真好吃')
}
// 使用Dog构造函数创建dog实例
const dog = new Dog('旺财', 3)
// 输出"肉骨头真好吃"
dog.eat()
// 输出"[object Object]"
dog.toString()
明明没有在 dog 实例里手动定义 eat 方法和 toString 方法,它们还是被成功地调用了。这是因为当我试图访问一个 JavaScript 实例的属性 / 方法时,它首先搜索这个实例本身;当发现实例没有定义对应的属性 / 方法时,它会转而去搜索实例的原型对象;如果原型对象中也搜索不到,它就去搜索原型对象的原型对象,这个搜索的轨迹,就叫做 原型链。
以我们的 eat 方法和 toString 方法的调用过程为例,它的搜索过程就是这样子的:
Q:那Object.prototype
的原型呢?
A:null,所以查到Object.prototype就可以停止查找了。
图中由相互关联的原型组成的链状结构就是原型链,也就是蓝色的这条线。
__proto__
, 绝大部分浏览器都支持这个非标准的方法访问原型,然而它并不存在与Person.prototype中,实际上,它是来自于Object.prototype,与其说是一个属性,不如说是一个getter/setter,当使用obj.proto时,可以理解成返回了Object.getPrototypeOf(obj)。