You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
varbook={title: 'your now Principles of Object-Oriented Javascript.'}===========**thesameasbelow**============varbook=Object.create(Object.prototype,{title: {configurable: true,enumerable: true,value: 'The Principles of Object-Oriented Javascript',writable: true,}});
functionPerson(name){// define a variable only accessible inside of the Person constructorletage=25;this.name=name;this.getAge=function(){returnage;};this.growOlder=function(){age+=1;};}constperson=newPerson('Nicholas');console.log(person.name);console.log(person.getAge());person.age=100;console.log(person.getAge());person.growOlder();console.log(person.getAge());console.log(person);
constPerson=(function(){// define a variable only accessible inside of the Person constructorletage=25;functionInnerPerson(name){this.name=name;}InnerPerson.prototype.getAge=function(){returnage;};InnerPerson.prototype.growOlder=function(){age+=1;};returnInnerPerson;}());constperson=newPerson('Nicholas');console.log(person.name);console.log(person.getAge());person.age=100;console.log(person.getAge());person.growOlder();console.log(person.getAge());console.log(person);
看一本js书好不好,主要看他对于面向对象的描述以及原型继承的描述。
第五章 继承
如何学习创建对象时理解面向对象js编程的第一步,而第二部是理解继承。
5.1 原型对象链和Object.prototype
JavaScript内建的继承方法被称之为原型对象链,又称之为原型对象继承。如果上一章所看,原型对象的属性可经由对象实例访问,这就是继承的一种形式。对象实例继承了原型对象的属性。因为原型对象也是一个对象。他也有自己的原型并继承其属性。因此可以说所有对象都继承自Object。
5.1.1 继承自 Object.prototype 的方法
前几章里用到的多个方法其实都是定义在Object.prototype上的。因此可以被其他对象继承,这些方法如下。
这5种方法经由继承出现在所有对象种。当需要让对象在JavaScript中以一致的方式工作,最后尤为重要,有时候你甚至会想要自己定义他们。
1. valueOf
返回原本的值。当每一个操作符被用于一个对象时候就会调用valueOf的方法。默认返回对象实例本身。。原始封装类型重写valueOf,对于字符串返回字符串本身,对于Boolean返回一个布尔值。对number返回一个数字。
上面这个例子,now是一个代表当前时间的Date,而earlier是一个过去的时间,当使用<比较的时候,在两个对象都调用了valueOf()的方法,另外,你甚至可以对比两个Date相减来获得他们在epoch时间上的差值。
2.toString()
一旦value()返回的是一个引用值而不是原始值的时候,就会回退调用toString()方法,另外,当JavaScript期待一个字符串的时候,也会对原始值隐式调用toString()。例如,当加号操作符的一边是一个字符串,另一边会被自动转化成字符串。如果另一边是一个原始值,就会自动转化成字符串。不懂请看下面例子,重写Object的toString原型方法,
'name'+{}; // namename
,字符串和布尔值相加会先将右边的布尔值转化成字符串,通过toString()这个方法。,首先如果右边是引用值,会先调用value的方法,如果value返回的还是一个引用值,那就调用toString()修改Object.prototype
修改Object会影响所有对象,这很危险。
这个新添加的属性是可枚举的.请看下面的例子,我给原型对象添加add方法,返回this+val,,这个会返回本对象并且因为他是引用值,所以会调用toString()方法,而toString方法又被我改写了,所以返回的就是'to stirng5'。
同时被新添加的add方法是实体字,说明他是可以被枚举的。
对象继承
对象继承是最简单的原型继承,你唯一需要做的就是指定新对象的[[Prototype]]指向原型对象。
同时可以使用Object.create()方法指定,他接受2个参数,第一个是需要被设置成新对象的[[Prototype]]的对象,,第二个参数和Object.definedProperties()中使用的一样。
两种声明具有相同效果,第一种使用了字面量自定义属性,而第二种使用了Object.create,显示用了同样的操作,这种默认继承无趣,但是如果你继承其他对象就有意思了。
person2继承person1 继承Object
假设你通过Object.create()创建时第一个参数为null,那么继承指向空。看下图,他没有任何方法,因此同时,他也是一个完美的hash容器,因为你给他命名任何方法,都可以,不存在任何冲突。
无法转换,因为不存在toString方法来进行隐式转换。一个很有意思,你可以通过它创建一个没有原型的对象。
构造函数继承
当你声明一个类的时候,JavaScript引擎默认帮你做了如下事情,
上面可以看出,你不需要做任何事情,这段代码帮你构造一个构造函数,其原型指向另一个继承自Function的的对象,并且它有一个新属性constructor,其值指向构造函数,如此循环下去,但是他们是一种循环式的自引用,众所周知,原型链仅仅只是一个指向对象的指针。这只是一种自己指向自己的循环引用point。下图例子说的很清楚,我这里只有一个a对象,a对象属性a指向它本身。那这就是循环自引用。而构造函数仅仅只是这样一个例子的复杂化而已。所有引用类型都是指针指向(point)
这个代码有两个构造函数:Rectangle和Square。Suqare构造函数的prototype属性被改写成Rectangle的一个对象实例。此时不需要给Rectangle的调用提供参数,因为他们不需要被使用,而且如果提供了,那么所有Square的对象实例都会享有共同的维度,用这种方法改变原型链,你需要确保构造函数不会再参数却是的时候抛出错误。(很多构造函数包含的初始化逻辑会需要参数)且构造函数不会改变任何全局状态。Square.prototype被改写后,constructor属性被重置为Square。
然后,rect作为Rectangle的实例对象呗创建,而square则被作为Square的实例创建。两个对象都有getArea()方法,因为他被继承自Rectangle.prototype和Object的对象实例。instanceof使用原型对象链检查对象类型。
Square.prototype并不需要真的被改写为一个Rectangle对象,毕竟Rectangle构造函数并没有真的为Square做什么必要的事情,事实上,唯一相关的部分是Square.prototype需要指向Rectangle.prototype,使得继承得以实现,这意味着你可以用Object.create()简化例子,代码如下
在这个版本的代码中,Square.prototype被改写成一个新的继承自Rectangle.prototype的对象,而Rectangle构造函数没有被调用。这意味着,你不再需要担心不参加构造函数会导致的错误。除此之外。
构造函数的窃取
啥意思?
由于JavaScript的继承通过原型对象继承来实现,因此不需要调用对象的父类的构造函数,如果你却是需要再子类构造函数中调用父类构造函数,那你就需要利用JavaScript函数工作的特性。
第二章提到过,call和apply方法允许你再调用函数是提供不同的this值,那正好是构造函数窃取的关键。而这个就是构造函数窃取的关键,只需要再子类构造函数中调用call或者apply调用父类的构造函数,并将新的创建的对象传进去即可。实际上,就是用自己的对象窃取父类的构造函数,如下例子。
Square构造函数调用了Rectangle构造函数,并传入了this和size量词,一次作为length,另一次作为width,这样做会在新的对象上创建length,和width属性并让他们等于size,这是一种避免再构造函数理重新定义你希望继承的属性的手段。你可以在调用完父类的构造函数后继续添加新属性覆盖已有的属性。
这分两步走的过程在你需要完成自定义类型之间的继承是比较有用,你经常需要修改一个构造函数的原型对象,你也经常需要在子类的构造函数中调用父类的构造函数的原型对象。一般,需要修改prototype来继承方法并且构造函数窃取来设置属性,由于这种做法模范了那些基于类的语言的类继承。,通常呗成为伪类继承。
5。5 访问父类方法
前面例子中,Square类型有自己的toString方法,,子类覆盖父类,但是如果你要访问父类怎么办???代替方法是在通过call或apply调用父类的原型对象的方法时传入一个子类的对象。
在这个版本的代码中,Square.prototype.toString()通过call调用Rectangle.prototype.toString()。该方法只需要在返回文本结果钱用"Square","Rectangle"。这种做法看起来有一点冗长,但是唯一有效访问父类的方法。
第六章 对象模式
终于讲到这一节,非常经典的啊。该书也只生写最后10页。精华就在后面,坚持看下去吧。
6.1 私有成员和特权对象
上面创建了匿名函数立即执行,。同时这意味着,这个函数仅存在于被调用瞬间,一旦执行就立即销毁了。IIFE常见的用于浏览器端环境打包模式。适用于模块化。
6.1.2 构造函数的私有成员
模块定义单个对象的是由属性上十分有效i,但对于那些同样需要私有属性的自定义类型又要怎么做?你可以在构造函数内部使用类似模块来创建每个实例的私有数据。如下例子:
上面代码中Person构造函数就有一个本地变量age。该变量被用于getAge()和growOlder()方法。当你创建Person的一个实例时候,该实例接受其自身的age变量,getAge()方法和growOlder()方法。这种做法很多时候都有类似模块模式,构造函数创建一个本地作用域返回this对象。上一章讨论过,将对象直接放在对象实例上不如放在其原型上面有效,如果你需要实例私有数据,这是唯一有效方法。
但是如果你需要所有实例都可以共享私有数据,就好像它被定义在原型上面那样,可以结合模块模式和构造函数,如下。
上面代码InnerPerson构造函数被定义在IIFE中。变量age被定义在构造函数外,但是在模块内部,并被两个原型对象的方法使用。IIFE返回InnerPerson构造函数作为全局作用域里面的Person构造函数使用,最终Person实例全部共享age作为闭包内部变量。
6.2 混入
JavaScript大量使用了伪类继承和原型对象继承,还有另一种就是混入。第一个对象接收者,通过直接赋值第二个对象提供者的属性从而接受了这些属性。
函数mixin()接受2个参数,接收者和提供者,,通过枚举方式,将所有可枚举的属性赋值给接收者,通过for...in循环所有可枚举属性。
作用域安全的构造函数
涨见识。。
如果不通过new就创建实例函数
上图为什么呢,因为它上了安全套
对于上面这个构造函数,当自己呗new调用的时候,就设置name属性,如果不被new调用的时候,则以new递归调用自己来为自己创建正确的属性。这么做就能保证行为一致性。
全书完。这是我写的最细的一本书了,短短92页,我每一页都给照抄下来了😭。
reference:JavaScript面向对象精要
The text was updated successfully, but these errors were encountered: