65.9K
CodeProject 正在变化。 阅读更多。
Home

挑战与解决方案 - 现代 Web 应用程序架构 - JavaScript - 第 2 部分

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.87/5 (38投票s)

2013 年 9 月 20 日

CPOL

13分钟阅读

viewsIcon

56916

JavaScript 是现代 Web 应用程序的重要组成部分。本文将概述 JavaScript 的一些功能。

文章系列

引言

本系列的第一部分展示了一些 CSS 3 和 JavaScript 的实际应用。现在,我们将更多地了解设备端的“忍者”,即 JavaScript。我们将从电池寿命、计算能力和存储空间有限的设备的角度来看待挑战和解决方案。

图 - 你想在哪里运行 JavaScript?

JavaScript 现状

JavaScript 是一种强大的语言,人们以各种风格编写代码。市面上有大量的脚本,脚本重用对于进步至关重要,无论它来自Brendan EichDouglas CrockfordRyan Dahl,还是其他人。为了合并现有框架,理解正在使用的各种风格非常重要。

图 - 从左到右:JavaScript 的创建者Brendan Eich、在 YUI 和 JSON 中做出重大贡献的Douglas Crockford,以及 Node.js 的创建者Ryan Dahl

在我继续之前,每当你遇到一个解释不清楚的例子时,我都建议在jsfiddle中进行尝试!:)

类和对象

在 JavaScript 中,你通过声明其构造函数来声明一个,而构造函数“不过是”一个“常规”的 JavaScript 函数。

function Button() {} // A Button class delcared

在 JavaScript 社区中,使用驼峰命名法(Title Case)来命名构造函数是一种约定。上面的代码声明了两件事:一个类 Button,以及它的构造函数。使用这个函数(或者如果你仍然想区分的话,构造函数),你可以使用new运算符创建 Button 对象。

var button1 = new Button();
var button2 = new Button();

类只是一个常规函数

上面的代码将分配两个内存位置,并执行 Button() 函数内的代码两次。如前所述,底层 Button 的“类”或“构造函数”不过是一个“常规”的 JavaScript 函数。事实上,“每个”JavaScript 函数都是Function类的一个实例。

var add = new Function("a", "b", "return a + b"); 
add(2, 6); // call the function 
// As function is object it can be used like a 'regular object'
// you can return function from a function or send it as argument
// and save it in a var
function addFactory()  {  
    var addMethod  = function (a, b)  {
        return a + b;
    }
    
    return addMethod;
}

var x = addFactory();

alert(x(1, 2)); // return 3

事实上,JavaScript 中的所有对象都继承自 Object;并从 Object.prototype 继承方法和属性,尽管它们可能会被覆盖。

typeof Button; // returns "function"
typeof button1; // returns "object"

button1 instanceof Button // true

带参数的构造函数

你可以像普通 JavaScript 函数一样,拥有一个带有参数的构造函数。

function Button(text) {
    
} 

类资产 - 方法和成员

一个类有两种资产成员方法。在下面的例子中,我们将逐步向 Button 类添加私有特权公共静态资产。我们还将初步了解继承和访问规则。

public

拥有类的公共成员

function Button(text) {
    this.text = text; // public member
}
var button = new Button("Submit");

button.text; // return 'Submit' 

私有的

以及拥有一个私有成员,即一个在公共方法中不可访问的成员。

function Button(text) {
    var visible; // private member
}

特权

并添加一个特权方法,即一个可以访问私有成员的方法。

function Button(text) {
    var visible;

    this.show = function() { // privileged method
    	visible = true;
    };
} 

重要的是要记住,私有成员是在不使用 this 的情况下声明和访问的。

私有方法

添加私有方法 hide()

function Button(text) {
    var visible;

    function hide() { // private method
    	visible = false;
    } 
} 

公共方法

并添加一个公共方法 resetText() 和一个公共成员 rounded

function Button(text) {
    this.text = text; // public member

    var visible; // private member

    this.show = function() { // privileged method
    	visible = true;
    };

    function hide() { // private method
    	visible = false; // INCORRECT: this.visible = false; do not use 'this' for private member
    }
}

Button.prototype.rounded = false; // public member

Button.prototype.resetText = function() { // public method can not access private assets
    this.text = ""; // INCORRECT: text = ""; always use 'this' for public member
}

公共特权之间的关键区别在于,特权方法是为类的每个实例创建的,而公共方法则在类的所有实例之间共享。这在创建数千个对象时很重要。我们稍后将对此进行更多讨论。

static

并添加一个静态成员或方法。

function Button(text) {
...   
}

Button.defaultText = "My Button"; // static member

Button.resetDefaultText = function() { // static method
    Button.defaultText = "";
};

继承(或链式调用)

图 - JavaScript 在所有链式对象中搜索方法或成员。

如前所述,每个 JavaScript 类只是一个常规函数。我们还知道,JavaScript 中的每个函数都是Function类的一个实例。Function类有一个公共属性prototype,用于将对象链式连接起来。当一个子对象被链式连接到父对象时,所有非私有父资产都表现得好像它们是子对象的一部分。如果子对象中没有可用的方法或成员,JavaScript 会在其父原型中查找,并且此搜索会一直持续到找到一个prototype = null 的对象。请查看此示例。

function A() {
   this.num = 100;    
}

function B() {
}

function C() {
}

B.prototype = new A(); // A is parent of B
C.prototype = new B(); // B is parent of C

var x = new C()
x.num; // return 100 search C-->C.prototype-->B.prototype-->A
x.speed; // return undefined search C-->C.prototype-->B.prototype-->A.prototype-->Object.prototype-->null

你可以看到,链式调用可以用于创建继承,如下所示。但在我们这样做之前,我想指出,深度链式调用可能会因为搜索而影响性能。此外,对于不存在的成员,搜索将一直持续到链的末端。

使用 new 继承

此示例展示了如何使用 newButton继承

function Button(text) {
    this.text = text; 
}

function LinkButton(text, url) {
    Button.call(this, text); // call base class constructor
    this.url = url;
}

LinkButton.prototype = new Button(); // this will make Button a base class

var b = new LinkButton("Google", "http://www.google.com");

b  instanceof Button; // return true
b instanceof LinkButton; // return true

b.text; // calling base class public member

使用 Object.create() 继承

图 - 大对象可以稍后使用 Object.call(this) 初始化。

new 运算符不仅创建 Button 对象,还调用其构造函数。在上例中,Button 构造函数被调用了两次,一次在 new Button(),然后是 Button.call(this, text)。这浪费了 CPU 周期。更好的方法是使用 Object.create(),它只创建对象而不调用构造函数。您之后可以使用 Object.call() 调用基类构造函数。

function Button(text) {
    this.text = text; 
}

function LinkButton(text, url) {
    Button.call(this, text); // call base class constructor
    this.url = url;
}

LinkButton.prototype = Object.create(Button); // only create object, do not call Button constructor

var b = new LinkButton("Google", "http://www.google.com");

b  instanceof Button; // return true
b instanceof LinkButton; // return true

b.text; // calling base class public member

嵌套与原型

现在是时候提到,Button 构造函数中的代码应该简短(并快速执行),以减少对象创建时间。考虑在智能手机上创建数百个 LinkButton 对象并将其添加到列表中的情况。在智能手机上,快速的对象创建+初始化将消耗更少的 CPU 周期,从而延长电池寿命,而电池寿命通常为 6 到 10 小时!

图 - 在电池寿命为 6 到 10 小时的 iPhone 上运行 JavaScript。

除了 CPU 使用导致的电池消耗外,嵌套在“构造函数”中的函数是为每个类的实例创建的,这将占用内存。

图 - 智能手机上的典型 RAM。

由于嵌套函数是为每个对象创建的,请尽可能将公共资产(即方法和成员)和初始化放在构造函数之外。这将使对象创建过程更快,并且占用的空间更小。

// Good
function Button(text) {
    this.text = text; 
}

Button.prototype.resetText = function() { // public method
    this.text = ""; 
};
// Bad
function Button(text) {
    this.text = text; 
    this.resetText = function() { // PROBLEM: public method declared as privilaged
        this.text = ""; 
    };
}

仅当它访问私有类资产时,或者当构造函数用提供的参数初始化成员时,才在构造函数内部声明方法。在所有其他情况下,请使用prototype来添加类资产。

图 - 近期的智能手机提供“桌面级”CPU。

尽管近期的智能手机提供了“桌面级”CPU,但嵌套会创建作用域级别,并减慢名称解析速度。

类和对象 - 动态添加或删除资产

图 - JavaScript 就像一件灵活的免费尺码 T 恤。

尽管 JavaScript 中的类充当对象创建的“蓝图”,但是您可以在创建对象之后添加方法和成员。“即时”类资产会立即添加到该类的“所有活动”对象中,而“即时”对象资产只会添加到“那个特定”对象实例中。例如,请考虑以下内容。

function Button(text) {
    this.text = text; 
}

var b1 = new Button("Tooltip Button");
var b2 = new Button("Normal Button");

b1.toolTip = "This tooltip public member is only for object 'b1'"; // on-the-fly 'object asset'
b2.toolTip; // ERROR: undefined

Button.prototype.resetText = function() { // on-the-fly 'class asset'
    this.text = ""; 
};

b1.resetText(); // OK
b2.resetText(); // OK too

图 - 添加类资产 - 在 Car 类中添加音响系统。

就像您可以向类或对象添加资产一样,您也可以删除它们。

delete b1.toolTip;

Button.prototype.resetText = undefined;

b1.toolTip; // undefined
b2.resetText(); // ERROR

图 - 删除对象资产 - 从 yourCar 对象中删除报警系统。

即时资产管理可能有助于使用异步模块定义 (AMD) 在线组合跨越多个 JavaScript 文件的对象。即使对于较小的对象,组合也可能有用,例如设备功能检测和必要的“修剪”对象。

封锁和冻结对象(非类)

图 - JavaScript 提供了多种方法来停止即时添加和删除资产。

当我们谈论 JavaScript、JScript 和 ECMAScript 时,它们是1 种语言还是 3 种?我想简要地触及这个主题,所以可以安全地说 ECMAScript 5 有“一种方法”来关闭对象中的即时资产添加和删除。

JavaScript 是一种由 Netscape 支持(现在是 Mozilla 支持)的语言,而 JScript 是微软开发的类似版本。1996 年,微软和 Netscape 同意基于 ECMA 标准开发他们各自的语言版本,称为ECMAScript或简称ES

在 ES5(或 JavaScript 1.8.5)兼容的浏览器中,您可以Seal()Freeze()一个对象。您无法在Sealed对象中添加或删除属性,而Freezing在使属性值不可编辑的同时,增加了密封的功能。如果您只想停止添加新属性,但现有属性可以删除,请使用preventExtensions()。Seal、Freeze 和 Prevent Extensions 不适用于类。请考虑以下示例。

function Button(text) {
    this.text = text; 
}

var b = new Button("My Button");

Object.seal( b ); 
Object.isSealed( b ); // returns true
b.text = "Submit"; // OK
b.toolTip = "Mine"; // FAIL silently
delete b.text; // FAIL silently
Button.prototype.toolTip = "Mine"; // OK, only object is sealed not Button class

Object.freeze( b ); 
Object.isFrozen( b ); // returns true
b.text = "Submit"; // FAIL silently
delete b.text; // FAIL silently
Button.prototype.toolTip = "Mine"; // OK, only object is freezed not Button class

Object.preventExtensions( b ); 
Object.isExtensible( b ); // returns false
b.text = "Submit"; // OK, you can still edit
delete b.text; // OK too
b.toolTip = "Mine"; // FAIL silently
Button.prototype.toolTip = "Mine"; // OK, only object is prevents extentions not Button class

资产的哈希表性质

图 - JavaScript 属性是键值对。

JavaScript 属性可以使用“索引”表示法、“键”表示法和“点”表示法声明。在不同上下文中,它们都是有用的构造。请参见下面的示例。

function Button(text) {
    this[0] = text; 
    this["is enabled"] = true; 
    this.focused = false; 
}

var b1 = new Button("My Button");

b1[0]; // return "My Button". Mostly used in loops
b1["is enabled"]; // return true. Mostly used for space separated, hypen'ed properties
b1.focused; // good ol dot notation return false

that

图 - 'that' 是一种将 'this' 传递给嵌套函数的方式。

在 JavaScript 中,私有方法无法访问特权资产。以下操作将不起作用。

function Button(text) {
     
     this.text = text; // privilaged member
     
     function isEmpty() {return this.text === ""; } // ERROR, this is not available in private methods
}   

为了提供必要的访问权限,JavaScript 社区使用了一个名为 'that' 的变量作为变通方法。

function Button(text) {
     this.text = text;
     var that = this; // store this in private var 'that'
     function isEmpty() {return that.text === ""; } // 'that' will work as it is a private member
}

一个更完整的例子可能是。

function Button(text) {
     
     this.text = text;
     
     var that = this;
     
     function isEmpty() {return that.text === ""; }
     
     this.getText = function() { return isEmpty() ? "NO TEXT" : this.text; };
}
     
var b1 = new Button("");     
     
b1.getText(); // return "NO TEXT"     

ASI - 自动分号插入

图 - 您可以用几乎不加分号的方式编写 JavaScript。

JavaScript 将“每行”视为一个新语句,除了少数情况。这意味着您不必在每行末尾添加分号,因为 JavaScript 会自动完成这项工作。这称为ASI或自动分号插入。因此,以下是一个完全有效的脚本。

function Button(text) {
     
     this.text = text
     
     var that = this
     
     function isEmpty() {return that.text === "" }
     
     this.getText = function() { return isEmpty() ? "NO TEXT" : this.text }
}
     
var b1 = new Button("")   
     
b1.getText() // return "NO TEXT"     
对于 return、throw、break 或 continue,JavaScript 会自动为新行插入分号。
// ASI if line ends with 'return'
function sum(a,b) { return // ASI here return;
a+b }

sum(1,3) // return undefined
// ASI if line ends with 'throw'
throw // ASI here throw;
"an error" 
// ASI if line ends with 'break'
var day=0

switch (day)
{
case 0:
  x = "Sunday"
  break // ASI here
case 1:
  x = "Monday"
  break
case 2:
  x = "Tuesday"
  break
case 3:
  x = "Wednesday"
  break
case 4:
  x = "Thursday"
  break
case 5:
  x = "Friday"
  break
case 6:
  x = "Saturday"
  break
}

alert(x) // return "Sunday"
// ASI if line ends with 'continue'
continue // ASI here continue;
以下是 JavaScript 将新行视为前一行的延续的地方,因此 JavaScript 不会自动为此类情况插入分号。
// No ASI for unclosed paren
function onclick(
e) 
// No ASI for unclosed array literal
var states = [
"NY", "MA", "CA"] 
// No ASI for unclosed object literal
var states = {
name : "Newyork", code : "MA"} 
// No ASI if ends with comma
var states,
cities 
// No ASI if ends with dot
from("state").
where("id='MA'").
select()
// No ASI if ends with ++ or --
var i = 1, j=5
i
++
j
// here i = 1 and j = 6
// No ASI for 'for'
for(var i = 0; i < 2; i++)
alert("hello") // part of loop
alert("world") // not a part of loop
// No ASI for 'while'
var i = 0;
while(i++ < 2)
alert("hello") // part of loop
alert("world") // not a part of loop
// No ASI for 'do'
var i = 0;
do
alert("hello") // part of loop, to put 2 or more statements use {}
while (i++ < 2)
// No ASI for 'if'
var i = 0
if(i == 0)
alert("hello") // alert will popup
// No ASI for 'else'
var i = 1;
if(i == 0)
alert("hello") 
else
alert("world") // alert will popup

没有类的对象

图 - 没有类型的对象。

JavaScript 是一种可以创建没有类的语言。其中最简单的是一个“空”对象,即没有任何资产的对象。您可以随时向空对象添加资产,也可以声明带有资产的对象。JavaScript 中的{}代表一个空对象或空对象字面量。

// empty object
var b={};

现在添加一些属性。

// object with properties
var b={text:"My Button", visible:false};
b.text; // return "My Button"

并添加一个方法。

// object with method
var b = {text:"My Button", visible:false, show: function() {this.visible = true;} };

b.show();  
b.visible; // returns true

您可以使用对象字面量符号从函数返回一个对象。

// a function returning object 
function getButton() {
    return {
        text: "My Button",
        visible: false,
        show: function () {
            this.visible = true;
        }
    }
}

getButton().text; // return "My Button"

您也可以嵌套对象。

// nested objects
var toolbar = {
    button1: {text: "My Button 1"}
   ,button2: {text: "My Button 2"}
}

toolbar.button2.text; // return "My Button 2"

命名空间 - 避免命名冲突

正如您所料,您的脚本将与其他脚本一起运行,因此将代码保存在一个命名空间中非常重要。到目前为止,我们所有的类都写在全局作用域中。让我们创建几个命名空间。

// create namespace. assign already created 'com' object else where or empty object {}
var com = com || {};
com.Acme = com.Acme || {};
com.Acme.UI = com.Acme.UI || {};
com.Acme.UI.Button = com.Acme.UI.Button || {};

现在我们有了 com.Acme.UI 命名空间,并且 Button 类已添加,现在是时候添加类资产了。但首先请注意,您不能在全局命名空间中创建构造函数 function Button()。所以,让我们看看您可以用匿名函数做的一些有趣的事情。

// an anonymous function declaration
function () {}

上面的代码将失败,因为。

  1. JavaScript 不期望匿名函数。
  2. JavaScript 期望语句的左侧和右侧都有内容。

另外,JavaScript 语句的最简单形式是。

// a semi 
;

让我们将匿名函数括在括号中,使其成为一个语句。

// a function statement
(function () {});

让我们回到我们的 Button 类。

// adding paren to make it a 'function statement'
com.Acme.UI.Button = (function () {});

如果在匿名函数末尾加上括号,它将立即执行。

// adding () to immidiately execute anonymous function
com.Acme.UI.Button = (function () {}());

现在我们有了一个匿名“容器”来保存我们的类。让我们添加一些“花哨”的功能。

// split one liner anonymous container in multi-line
com.Acme.UI.Button = (function () {

}());

// add constructor
com.Acme.UI.Button = (function () {
    function Button(text) {
       
    }
}());

// export Button class (simply return Button)
com.Acme.UI.Button = (function () {
    function Button(text) {
      
    }
    
    return Button;
}());

// add public property
com.Acme.UI.Button = (function () {
    function Button(text) {
       this.text = text;
    }
    
    return Button;
}());

// add public, private and privileged assets
com.Acme.UI.Button = (function () {

    // constructor
    function Button(text) {
        this.text = text; // public property
        var that = this;

        // private method
        function isEmpty() {
            return that.text === "";
        }

        // privileged method
        this.getText = function () {
            return isEmpty() ? "NO TEXT" : this.text;
        };
    }

    // public method
    Button.prototype.resetText = function () {
        return this.text = "";
    }

    return Button;
}());

var b = new com.Acme.UI.Button("My Button");
b.getText(); // return "My Button"
b.resetText();
b.getText(); // "return "NO TEXT"

库或模块

您可以使用前面部分中使用的“匿名容器”来创建跨越多个文件的 JavaScript 库。模块提供了一个作用域,您可以在其中拥有公共私有成员。

var LIBRARY = (function () {
    // all global variables will be available here
    // however assets in this scope will not conflict 
    // with global scope as they are private
});

var MODULE = (function (jQuery, underscore) {
    // import libraries with different name here
    // to avoid any conflicts. Access to imported
    // variable will be faster as compared to 
    // global access
}($, _));

我们可以创建简单的Util库,如下所示。

var Util = (function (u) {
    // private 
    var key = "secret";

    // private method
    function hash() {
        return 123;
    }

    // public property
    u.version = "1.0";

    // public method
    u.productName = function () {
        return "Acme Library";
    }

    return u;
}(Util || {}));

Util.productName(); // return "Acme Library"

以下是隐藏私有状态并导出公共方法的另一种方法。请注意,在调用匿名函数时会返回一个对象。

var Counter = (function () {
    var privateCounter = 0;

    function changeBy(val) {
        privateCounter += val;
    }
    
    return {
        increment: function () {
            changeBy(1);
        },
        decrement: function () {
            changeBy(-1);
        },
        value: function () {
            return privateCounter;
        }
    }
})();

Counter.increment();

alert(Counter.value()); // return 1

方法链式调用

方法链式调用使您能够一个接一个地调用对象上的方法。如果您不返回任何内容,则方法返回this

var obj = {
    value: 1,
    increment: function () {
        this.value += 1;
        return this;
    },
    add: function (v) {
        this.value += v;
        return this;
    },
    shout: function () {
        console.log(this.value);
    }
};

// chain method calls
obj.increment().add(3).shout(); // 5

// As opposed to calling them one by one
obj.increment();
obj.add(3);
obj.shout();

简洁的 getter 和 setter

JavaScript 通过 getset 为属性 get 和 set 方法提供了简洁的语法。

var carStero = {
    _volume: 0,

    get volume() {
        return this._volume;
    },

    set volume(value) {
        if (value < 0 || value > 100) {
            throw new Error("Volume shold be between 0-100");
        }

        this._volume = value;
    }
};

try {
    carStero.volume = 110;
} catch (err) {
    alert(err.message);
}

全局变量

图 - 最大限度地减少全局空间的使用。

您可以非常容易地声明全局变量。

var width = 100; // global
height = 50; // global without var

delete width; // ERROR
delete height; // OK

var states = ["CA", "NY", "MA"];
delete states[1]; // OK, delete NY
states[1]; // return undefined
states.length; // return 3

delete运算符删除数组元素时,该元素就不再位于数组中,访问它将返回undefined。但是,它仍然是可寻址的。

任何没有var的成员都会进入全局作用域。

height; // global

function Button(text) {
  var label = caption = text; // 'label' is private but without var 'caption' is global

  caption = text; // without var 'caption' is global
}

ECMAScript 5 严格模式

John Resig 对 ES5 严格模式进行了出色的解释。他这样说:

图 - 严格模式基本上有助于减少代码编写错误。
"use strict";

在严格模式下,删除变量、函数或参数将导致错误。

var foo = "test";

function test() {}

delete foo; // Error 
delete test; // Error 

function test2(arg) {
    delete arg; // Error 
}

在对象字面量中多次定义属性或函数参数将导致抛出异常。

// ERROR
{
    foo: true,
    foo: false
}

function (foo, foo) {} //ERROR

几乎所有尝试使用名称 'eval' 的行为都被禁止——并且无法将 eval 函数分配给变量或对象的属性。

// All generate ERROR
obj.eval=... 
new Function("eval") 

此外,通过 eval 引入新变量的尝试将被阻止。

eval("var a=false;"); 
print( typeof a ); // undefined

最后,一个长期存在的(并且非常烦人的)错误已经得到解决:null 或 undefined 被强制转换为全局对象的情况。严格模式现在可以防止这种情况发生,并会抛出异常。

(function(){ ... }).call( null ); // Exception

当启用严格模式时,with(){} 语句将失效——事实上,它甚至会显示为语法错误。

函数参数

有一个arguments对象,它仅在函数体内可用。这可能有助于创建多个类构造函数或实用函数,这些函数会遍历所有arguments来执行某项任务,例如排序。

function sort() {
    alert(arguments.length);
    
    arguments[1] = 5; // 1, 5, 3 
    
    alert(arguments[1]);
} 
sort(7,2,3);
sort("A","B","C", "D");

闭包

随着嵌套 JavaScript 函数的出现,出现了闭包的能力,即在函数返回后仍然保留堆栈上的变量。在正常情况下,当函数返回时,它的堆栈会被销毁,所有变量也会如此。对于一个典型的函数,一旦它返回到调用环境,它所有的局部变量和参数都符合垃圾回收的条件。

当一个函数的局部变量函数参数在函数返回后仍然保留时,就会发生闭包。考虑以下返回匿名函数的函数。匿名内部函数会记住min的值在它返回时是什么,即使内部函数在代码后面被调用。

function volumeFactory(min) {
    return function (value) {
        return value + min;
    }
}

var mode = "meeting";
var funcSetVolume;

if (mode === "meeting") {
    funcSetVolume = volumeFactory(10);
} else {
    funcSetVolume = volumeFactory(50);
}

alert(funcSetVolume(1)); // min has a clousure, so min=10 and min + 1 = 11

闭包是一种特殊的组合对象:一个函数,以及创建该函数的环境。环境包括在创建闭包时作用域内的任何局部变量。我没有提到,但前面讨论的命名空间模块都是闭包的例子。可以在此处看到一个更有趣的闭包示例。

JavaScript 是一种 OO 语言吗?

开发人员熟悉继承的概念,即“派生类是一种基类”。他们从学校教育和计算机科学教科书中了解到这一点。因此,他们习惯于认为类 Car继承 Vehicle {},而类 Truck继承 Vehicle {}Vehicle作为CarTruck基类的父子是一种关系的快速 OO 实现。

当这些开发人员尝试在 JavaScript 中进行 OO 时,他们会意识到除了之外,还有朋友。朋友之间的关系是,如果你不知道如何自己做某事,就向其他朋友寻求帮助!所以,从某种意义上说,JavaScript 的基于原型的链式调用表明父对象实际上是其子对象的朋友:)但这只是一个观点。

开发人员擅长思考和建模父子关系,他们这样做的原因有两个:A) 更好地理解代码,B) 重用代码。您已经看到 JavaScript 以一种略有不同的方式提供了多种方法来完成这两件事。一旦您掌握了这门语言,您就会爱上它。

我只是粗略地介绍了 JavaScript 的一些功能。希望在后续的文章中能看到更多它的强大之处。

下一步

起初,我曾考虑在这部分内容中涵盖 Backbone、Angular、Knockout 等 JavaScript 框架,但似乎最好将它们留待下次讨论,并使这一部分专门介绍 JavaScript。我们将在下次深入研究这些流行的框架,并看看它们解决了什么问题或带来了什么挑战。

© . All rights reserved.