JavaScript- The Good Parts Chapter 5 Inheritance

Divides one thing entire to many objects;
Like perspectives, which rightly gazed upon
Show nothing but confusion. . .
—William Shakespeare, The Tragedy of King Richard the Second

Inheritance is an important topic in most programming languages.

In the classical languages (such as Java), inheritance (or extends) provides two useful services. First, it is a form of code reuse. If a new class is mostly similar to an existing class, you only have to specify the differences. Patterns of code reuse are extremely important because they have the potential to significantly reduce the cost of software development. The other benefit of classical inheritance is that it includes the specification of a system of types. This mostly frees the programmer from having to write explicit casting operations, which is a very good thing because when casting,the safety benefits of a type system are lost.

JavaScript, being a loosely typed language, never casts. The lineage of an object is irrelevant. What matters about an object is what it can do, not what it is descended from.

JavaScript provides a much richer set of code reuse patterns. It can ape(模仿) the classical pattern, but it also supports other patterns that are more expressive. The set of possible inheritance patterns in JavaScript is vast. In this chapter, we’ll look at a few of the most straightforward patterns. Much more complicated constructions are possible,but it is usually best to keep it simple.

Pseudoclassical

JavaScript is conflicted about its prototypal nature. Its prototype mechanism is obscured by some complicated syntactic business that looks vaguely classical.Instead of having objects inherit directly from other objects, an unnecessary level of indirection is inserted such that objects are produced by constructor functions.

When a function object is created, the Function constructor that produces the function object runs some code like this:

this.prototype = {constructor: this};

The new function object is given a prototype property whose value is an object containing a constructor property whose value is the new function object. The prototype object is the place where inherited traits are to be deposited. Every function gets a prototype object because the language does not provide a way of determining which functions are intended to be used as constructors. The constructor property is not useful. It is the prototype object that is important.

When a function is invoked with the constructor invocation pattern using the new prefix, this modifies the way in which the function is executed. If the new operator were a method instead of an operator, it could have been implemented like this:

Function.method(‘new‘, function ( ) {
// Create a new object that inherits from the
// constructor‘s prototype.
    var that = Object.create(this.prototype);
// Invoke the constructor, binding –this- to
// the new object.
    var other = this.apply(that, arguments);
// If its return value isn‘t an object,
// substitute the new object.
    return (typeof other === ‘object‘ && other) || that;
});

We can define a constructor and augment its prototype:

var Mammal = function (name) {
    this.name = name;
};
Mammal.prototype.get_name = function ( ) {
    return this.name;
};
Mammal.prototype.says = function ( ) {
    return this.saying || ‘‘;
};

Now, we can make an instance:

var myMammal = new Mammal(‘Herb the Mammal‘);
var name = myMammal.get_name( ); // ‘Herb the Mammal‘

We can make another pseudoclass that inherits from Mammal by defining its constructor function and replacing its prototype with an instance of Mammal:

var Cat = function (name) {
    this.name = name;
    this.saying = ‘meow‘;
};
// Replace Cat.prototype with a new instance of Mammal
Cat.prototype = new Mammal( );
// Augment the new prototype with
// purr and get_name methods.
Cat.prototype.purr = function (n) {
    var i, s = ‘‘;
    for (i = 0; i < n; i += 1) {
        if (s) {
            s += ‘-‘;
        }
        s += ‘r‘;
    }
    return s;
};
Cat.prototype.get_name = function ( ) {
    return this.says( ) + ‘ ‘ + this.name +
            ‘ ‘ + this.says( );
};
var myCat = new Cat(‘Henrietta‘);
var says = myCat.says( ); // ‘meow‘
var purr = myCat.purr(5); // ‘r-r-r-r-r‘
var name = myCat.get_name( );
//            ‘meow Henrietta meow‘

The pseudoclassical pattern was intended to look sort of object-oriented, but it is looking quite alien. We can hide some of the ugliness by using the method method and defining an inherits method:

Function.method(‘inherits‘, function (Parent) {
    this.prototype = new Parent( );
    return this;
});

Our inherits and method methods return this, allowing us to program in a cascade style. We can now make our Cat with one statement.

var Cat = function (name) {
    this.name = name;
    this.saying = ‘meow‘;
}.
    inherits(Mammal).
    method(‘purr‘, function (n) {
        var i, s = ‘‘;
        for (i = 0; i < n; i += 1) {
            if (s) {
                s += ‘-‘;
            }
            s += ‘r‘;
        }
        return s;
    }).
    method(‘get_name‘, function ( ) {
        return this.says( ) + ‘ ‘ + this.name +
                ‘ ‘ + this.says( );
    });

By hiding the prototype jazz, it now looks a bit less alien. But have we really improved anything? We now have constructor functions that act like classes, but at the edges, there may be surprising behavior. There is no privacy; all properties are public. There is no access to super methods.

Even worse, there is a serious hazard with the use of constructor functions. If you forget to include the new prefix when calling a constructor function, then this will not be bound to a new object. Sadly, this will be bound to the global object, so instead of augmenting your new object, you will be clobbering global variables. That is really bad. There is no compile warning, and there is no runtime warning.

This is a serious design error in the language. To mitigate this problem, there is a convention that all constructor functions are named with an initial capital, and that nothing else is spelled with an initial capital. This gives us a prayer that visual inspection can find a missing new. A much better alternative is to not use new at all.

The pseudoclassical form can provide comfort to programmers who are unfamiliar with JavaScript, but it also hides the true nature of the language. The classically inspired notation can induce programmers to compose hierarchies that are unnecessarily deep and complicated. Much of the complexity of class hierarchies is motivated by the constraints of static type checking. JavaScript is completely free of those constraints. In classical languages, class inheritance is the only form of code reuse.JavaScript has more and better options.

时间: 2024-10-10 11:44:14

JavaScript- The Good Parts Chapter 5 Inheritance的相关文章

JavaScript: The Evil Parts - 1

最近在看JavaScript框架设计,在讲解类型判定的时候提到了一些“匪夷所思的情况”,不过没有明说都是什么时候会出现这些情况.自己玩儿了一下,写写随笔吧.不过可能除了我找到的,还有会其他时候会出现这些诡异的现象2333 问题:在JavaScript中,什么时候会出现 a !== a a == b && b != a a == !a a == b && a == c && b != c a != b && a != c &&

JavaScript: The Good Parts

Chapter 1 Good Parts: JavaScript is an important language because it is the language of the web browser. The very good ideas include functions, loose typing, dynamic objects, and an expressive object literal notation. The bad ideas include a programm

JavaScript- The Good Parts CHAPTER 2

I know it well:I read it in the grammar long ago.—William Shakespeare, The Tragedy(悲剧:灾难:惨案) of Titus Andronicus This chapter introduces the grammar of the good parts of JavaScript, presenting a quick overview of how the language is structured. We wi

JavaScript- The Good Parts Chapter 4

Why, every fault’s condemn’d ere it be done:Mine were the very cipher of a function. . .—William Shakespeare, Measure for Measure The best thing about JavaScript is its implementation of functions. It got almost everything right. But, as you should e

JavaScript- The Good Parts Chapter 6

Thee(你) I’ll chase(追逐:追捕) hence(因此:今后), thou(你:尔,汝) wolf in sheep’s array.—William Shakespeare, The First Part of Henry the SixthAn array is a linear(线的,线型的:直线的,线状的:长度的) allocation(分配,配置:安置) of memory in which elements are accessed by integers that a

JavaScript- The Good Parts Chapter 3 Objects

Upon a homely object Love can wink.—William Shakespeare, The Two Gentlemen of Verona The simple types of JavaScript are numbers, strings, booleans (true and false), null,and undefined. All other values are objects. Numbers, strings, and booleans are 

读 《JavaScript: The Good Parts》 有感

提炼出一门语言或技术的 Good Parts, 使用该子集去构造健壮稳固的应用. 我们总是倾向于去学习和使用所有的语言特性,好像凡是新的,凡是提供了的, 就有必要去使用: 这本书告诉我们, 要有选择性地学习和使用. 不是所有的语言特性都需要学习和使用. 学习和使用那些设计不良的特性,不仅耗费大量时间和精力,而且有损项目的可维护性,得不偿失:反之,学习那些优良的部分,不仅可以节省时间,腾出更多时间和精力去做更重要的事情,而且有助于将事情做好,提高收益/学习比. 富有技巧性的HACKER可以欣赏其精

Chapter 5 Inheritance

1. Classes, Superclasses, and Subclasses 2. Objects: The Cosmic Superclass 3. Generic Array Lists 4. Objects wrappers and Autoboxing 5. Methods with a Variable Number of Parameters 6. Enumeration Classes 7. Reflection 8. Design Hints for Inheritance

《JavaScript高级程序设计》Chapter 15 canvas + Chapter 16 HTML5

Chapter 15 Canvas Chapter 16 HTML5 Chapter 15 Canvas <canvas>元素:设定区域.JS动态在这个区域中绘制图形. 苹果公司引导的.由几组API构成. 2D上下文普及了.WebGL(3D上下文)还未足够普及. 基本用法 首先:width.height属性确定绘图区域大小.后备信息放在开始和结束标签之间. getContext():DOM获得这个canvas元素对象.再在这个对象上调用getContext()获取上下文,传入参数表示获取的是2