网站内链设计,手机html5网站模板,网站知识介绍,网上商城开发费用帮助面向对象开发者理解关于JavaScript对象机制 本文是以一个熟悉OO语言的开发者视角#xff0c;来解释JavaScript中的对象。 对于不了解JavaScript 语言#xff0c;尤其是习惯了OO语言的开发者来说#xff0c;由于语法上些许的相似会让人产生心理预期#xff0c;JavaScrip… 帮助面向对象开发者理解关于JavaScript对象机制 本文是以一个熟悉OO语言的开发者视角来解释JavaScript中的对象。 对于不了解JavaScript 语言尤其是习惯了OO语言的开发者来说由于语法上些许的相似会让人产生心理预期JavaScript中的原型继承机制和class语法糖是让人迷惑的。 如果你已经对prototype机制已有了解但是由于两者对象机制的巨大本质差异对它和构造函数实例对象的关系仍有疑惑本文或许可以解答你的问题。 我们看下面的代码可以看出和OO语言相比语法上也有很大分别 // 定义一个类
class Foo {constructor() {this.a a;}
}//实例化对象
const foo new Foo();//定义原型的属性
Foo.prototype.b b;//实例可以访问属性
foo.b // b//修改原型的属性
Foo.prototype.b B;//实例属性值没有被修改
foo.b // b 类已经定义了怎么还能修改呢prototype又是什么 不存在面向对象 对于熟悉了面向对象的开发者而言JS中种种非预期操作的存在都是因为JavaScript中根本没有面向对象的概念只有对象没有类。 即使ES6新添了class语法不意味着JS引入了面向对象只是原型继承的语法糖。 原型是什么 什么是原型如果说类是面向对象语言中对象的模版原型就是 JS中创造对象的模版。 在面向类的语言中实例化类就像用模具制作东西一样。实例化一个类就意味着“把类的形态复制到物理对象中”对于每一个新实例来说都会重复这个过程。 但是在JavaScript中并没有类似的复制机制。你不能创建一个类的多个实例只能创建多个对象它们[[Prototype]]关联的是同一个对象。 //构造函数
function Foo(){
}
//在函数的原型上添加属性
Foo.prototype.prototypeAttribute0 {status: initial};const foo0 new Foo();
const foo1 new Foo();
foo0.prototypeAttribute0 foo1.prototypeAttribute0 //true 对象、构造函数和原型的关系 当我们创建一个新对象的时候发生了什么对象、构造函数和原型到底什么。 先简单地概括 原型用于定义共享的属性和方法。 构造函数用于定义实例属性和方法仅负责创造对象与对象不存在直接的引用关系。 我们先不用class语法糖这样便于读者理解和暴露出他们之间真正的关系。 // 先创建一个构造函数 定义原型的属性和方法
function Foo() {this.attribute0 attribute0;
} 当创建了一个函数就会为该函数创建一个prototype属性它指向函数原型。 所有的原型对象都会自动获得一个constructor属性这个属性的值是指向原型所在的构造函数的指针。 现在定义原型的属性和方法
Foo.prototype.prototypeMethod0 function() {console.log(this is prototypeMethod0);
}Foo.prototype.prototypeAttribute0 prototypeAttribute0; 好了现在新建一个对象 const foo new Foo();foo.attribute0 // attribute0
foo.prototypeAttribute0 //prototypeAttribute0
foo.prototypeMethod0() // this is prototypeMethod0 它拥有自己的实例属性attribute0并且可以访问在原型上定义的属性和方法他们之间的引用关系如图所示。 当调用构造函数创建实例后该实例的内部会包含一个指针内部对象指向构造函数的原型对象。 当读取实例对象的属性时会在实例中先搜寻没有找到就会去原型链中搜索且总是会选择原型链中最底层的属性进行访问。!--原型对象自己也可以有原型对象这样就构成了原型链。关于原型链这里不作过多介绍-- 对象的原型可以通过__proto__在chrome等浏览器上访问。 __proto__是对象的原型指针prototype是构造函数所对应的原型指针。 语法糖做了什么 ES6推出了class语法为定义构造函数和原型增加了便利性和可读性。 class Foo {constructor(){this.attribute0 attribute0;}prototypeMethod0(){console.log(this is prototypeMethod0)}
}/* 相当于下面的声明*/
function Foo() {this.attribute0 attribute0;
}Foo.prototype.prototypeMethod0 function() {console.log(this is prototypeMethod0)
} class中的constractor相当于构造函数而class中的方法相当于原型上的方法。、 值得注意的特性 属性屏蔽 —— 避免实例对象无意修改原型 看这段代码思考输出的结果。 class Foo {prototypeMethod0(){console.log(this is prototypeMethod0)}
}const foo0 new Foo();
const foo1 new Foo();foo0.prototypeMethod0 foo0.__proto__.prototypeMethod0 // truefoo0.prototypeMethod0 () console.log(foo0 method);
foo0.prototypeMethod0(); //??
foo1.prototypeMethod0(); //??
foo0.prototypeMethod0 foo0.__proto__.prototypeMethod0 // ?? 输出的结果是 foo0.prototypeMethod0(); // foo0 method
foo1.prototypeMethod0(); // this is prototypeMethod0
foo0.prototypeMethod0 foo0.__proto__.prototypeMethod0 // false 我们知道对象即便是原型对象都是运行时的。 创建之初foo本身没有prototypeMethod0这个属性访问foo0.prototypeMethod0将会读取foo0.__proto__.prototypeMethod0。 直接修改foo0.prototypeMethod0没有改变__proto__上的方法原因是存在属性屏蔽。 现在的情况是想要修改foo0.prototypeMethod0prototypeMethod0在foo中不存在而在上层即foo.__proto__中存在并且这不是一个特殊属性如只读。 那么会在foo中添加一个新的属性。 这便是为什么直接修改却没有影响__proto__的原因。 !--更多属性屏蔽的场景也不做赘述-- 小结 再温习一遍这些定义 原型用于定义共享的属性和方法。 构造函数用于定义实例属性和方法仅负责创造对象与对象不存在直接的引用关系。 __proto__是对象的原型指针prototype是构造函数的原型指针。 在解释原型作用的文章或书籍中我们会听到继承这样的术语其实更准确地委托对于JavaScript中的对象模型来说是一个更合适的术语。 委托行为意味着某些对象在找不到属性或者方法引用时会把这个请求委托给另一个对象。对象之间的关系不是复制而是委托。 参考 《JavaScript高级程序设计》 《你不知道的JavaScript》 本文仅供解惑要在脑袋里形成系统的概念还是要看书呀。 有疑问欢迎大家一起讨论。