tip:有问题或者需要大厂内推的+我脉脉哦:丛培森 ٩( ‘ω’ )و
JavaScript语言是面向对象的语言, 但是它是基于原型的面向对象语言
这和C++等一些语言不同, 它实际上并没有"类", 不能实例化"类"
原型编程的基本原则:
- 所有的数据都是对象
- 得到一个对象,并不是通过实例化类,而是找到一个对象作为原型并继承它
- 对象会记住它的原型
- 如果对象无法响应某个请求,它会把这个请求委托给自己的原型
下面就来我们就来看看JavaScript是怎样以这样的规则构建对象的
1. 所有数据都是对象
我们不能说JavaScript中所有数据都是对象, 但我们可以说绝大部分数据都是对象
除了undefined以外, number、string、boolean都可以通过基本包装类变成对象来处理
JavaScript中有一个根对象Object.prototype
对象实际上都是从这个根对象克隆来的
而Object.prototype就是它们的原型
2. 得到一个对象,不是通过实例化类,而是找到一个对象作为原型并继承它
我们要想构建一个"空"对象, 可以通过构造函数var obj = new Object()或者对象字面量var obj = {}
构建时, JavaScript引擎内部会从Object.prototype上克隆一个对象出来
3. 对象会记住它的原型
JavaScript中的原型链机制允许在原型链上逐层查找,所以每个节点都必须知道它的下一个节点
即每个对象都应该记住自己的原型
我们一直说"对象的原型", 其实并不准确
准确的说是对象的构造器有原型
JavaScript中对象都有隐藏属性_proto_
它默认指向该对象的构造器的原型对象
4. 如果对象无法响应某个请求,它会把这个请求委托给自己的原型
当一个对象无法响应某个请求是, 它会沿着原型链把请求传递下去, 知道遇到可以处理请求的对象为止
虽然JavaScript的对象那个最初都是由Object.prototype对象克隆而来的, 但对象构造器的原型不仅限于Object.prototype, 而是可以动态指向其他对象
这样, 当对象a需要用到b的能了是, 可以改变对象a构造器的原型指向b
从而达到继承的效果
下面代码是我们常用的原型继承
var obj = {name: 'payen'};
var A = function(){};
A.prototype = obj;
var a = new A();
console.log(a.name);//输出payen
这段代码执行时, 引擎做了什么呢
- 首先, 尝试比那里a中所有属性, 没找到name属性
- "查找name属性"的请求被委托给对象a的构造器的原型, 它被__proto__记录并指向A.prototype, 而A.prototype被设置为对象obj
- 在对象obj中找到了name属性, 返回它的值
下面是chorme浏览器控制台打印的b
当我们想让一个"类" 继承另一个"类"时, 可以这样做
var A = function(){};
A.prototype = {
name: 'payen'
};
var B = function(){};
B.prototype = new A();
var b = new B();
console.log(b.name);//输出payen
我们来分析一下执行这段代码时, 引擎做了什么
- 首先, 尝试遍历b中所有属性, 遗憾的是没找到name属性
- "查找name属性"的请求被委托给对象b的构造器的原型, 它记录在b.__proto__中并且指向B.prototype中, 而B.prototype设置为通过new A()创建出来的对象
- 在这个对象中依然没找到name, 于是请求被继续委托个这个对象构造器的原型A.prototype
- 在A.prototype中找到了name属性, 返回它的值
和把B.prototype直接指向一个对象字面量相比
通过B.prototype = new A() 在原型链上多形成一层
但其实没什么区别
如果访问的不是name而是一个不存在的值, 那么请求就会被传递到Object.prototype的顶级原型依然没找到,返回undefined
下面是chorme浏览器控制台打印的b