现代Web应用程序JavaScript第 2部分的挑战和解决

分享于 

34分钟阅读

Web开发

  繁體

系列文章

介绍

本系列的第1部分展示了一小部分CSS和 JavaScript。 现在我们看到了更多的设备端'忍者'换句话说,JavaScript。 我们将在电池寿命有限。计算能力和存储能力有限的设备的视角下看到挑战和解决方案。

你想运行JavaScript的?

JavaScript今日

JavaScript是一种强大的语言,人们在各种风格的语言中编写代码。 这里面有很多脚本,脚本重用对前进很重要,可能是来自 Brendan EichDouglas Crockford,Ryan,或者其他任何人。 要合并现有框架,我们必须了解使用的各种样式,这很重要。

- 左至右: 他的作者 Eich是JavaScript的创建者,Douglas Crockford 主要贡献和 JSON, Ryan Dahl的创建者 of

在我继续之前,只要你发现一个很糟糕的例子,我建议你在 jsfiddle中使用它。 ! : )

类和对象

在JavaScript中,通过声明类的构造函数( 它是'无',而不是'常规'JavaScript函数),声明了一个类。

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

在JavaScript社区中,使用构造函数函数的标题是一个惯例。 上面 代码声明了两种情况: 类 Button 及其构造函数。 使用这里函数(。如果你仍然希望区分),你可以使用新的运算符创建 Button 对象:

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

一个类只是一个常规函数

代码 上面 将分配两个内存位置,并在 Button() 函数两次执行代码 inside。 正如前面提到的,在 Button'类'或者'构造函数函数'下面,只有一个'常规'javascript函数。 实际上,'every'函数是 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 varfunction addFactory() { 
 var addMethod = function (a, b) {
 return a + b;
 }
 return addMethod;
}var x = addFactory();
alert(x(1, 2)); // return 3

实际上,JavaScript中的所有对象都来自 Object ;inherit 方法和 Object.prototype 中的属性,尽管它们可能被重写。

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

带参数的构造函数

你可以拥有带有一些参数的构造函数,就像常规的JavaScript函数一样:

function Button(text) {
} 

类资产- 方法和成员

一个班级有两种资产: 成员和方法。 在以下例子中,我们将逐步增加 private privilegedprivileged,并在一个 Button 类中收取 我们还将首先查看继承和访问规则。

public

要有 public 成员:

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

private

如果要拥有一个 private 成员 换句话说,则在 public 方法中不可访问的成员:

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

特权

要添加收费的特权方法 换句话说,可以访问 private 成员

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

需要记住的一点是,在不使用 this的情况下声明和访问了个 private 成员。

private 方法

要添加的方法 hide(),请执行以下操作:

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

public 方法

要添加 public 方法 resetText() 和 public 成员 rounded:

function Button(text) {
 this.text = text; // public membervar visible; // private memberthis.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 memberButton.prototype.resetText = function() { // public method can not access private assetsthis.text = ""; // INCORRECT: text =""; always use 'this' for public member}

public 方法中,每个实例的实例共享的特权方法,这是和特权的关键差异。 如果创建了数千个对象,这就变得非常重要。 稍后我们将进一步了解。

static

要添加的成员或者方法:

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

inherit ( 或者链)

对所有链对象中的方法或者成员进行

如前所述,每个JavaScript类都只是一个常规函数。 我们还知道,JavaScript中的每个函数都是 Function 类的一个实例。Function 类有一个 public 属性 prototype Prototype,它用于将chain对象 together together。 当子对象链接到父对象时,所有非私有父级资产就像它们是子对象的部分一样。 在子对象中没有方法或者成员时,JavaScript将查看其父 Prototype,直到找到具有 prototype =的对象时,该搜索继续。 签出这里示例:

function A() {
 this.num = 100; 
}function B() {
}function C() {
}
B.prototype = new A(); // A is parent of BC.prototype = new B(); // B is parent of Cvar x = new C()
x.num; // return 100 search C-->C.prototype-->B.prototype-->Ax.speed; // return undefined search C-->C.prototype-->B.prototype-->A.prototype-->Object.prototype-->null

你可以看到,链接可以用来创建继承,我们将看到 below。 但是在我们这样做之前,我想指出深度链接可能会因为搜索而影响性能。 对于不存在的成员,搜索将继续直到链的末尾。

inherit 使用新

本示例演示如何使用从 Button 类向 inherit 收费:

function Button(text) {
 this.text = text; 
}function LinkButton(text, url) {
 Button.call(this, text); // call base class constructorthis.url = url;
}
LinkButton.prototype = new Button(); // this will make Button a base classvar b = new LinkButton("Google", "http://www.google.com");
b instanceof Button; // return trueb instanceof LinkButton; // return trueb.text; // calling base class public member
inherit 使用 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 constructorthis.url = url;
}
LinkButton.prototype = Object.create(Button); // only create object, do not call Button constructorvar b = new LinkButton("Google", "http://www.google.com");
b instanceof Button; // return trueb instanceof LinkButton; // return trueb.text; // calling base class public member

嵌套 vs Prototype

可能需要提及的是,Button 构造函数的代码 inside 应该是短( 并快速执行),以减少对象创建时间。 考虑创建数百个 LinkButton 对象并将它们添加到智能电话列表中的案例。 在智能手机上,快速创建对象+ 初始化将吃掉 LESS CPU周期,导致电池寿命增加,通常为 6到 10小时。

在iphone上运行个小时,电池寿命为小时

除了CPU消耗,还会为每个的新的实例创建一个嵌套在'构造函数函数'内部的函数,该函数将税收内存 tax tax。

智能电话系统中典型的内存

为每个对象构造创建嵌套函数,在可能的情况下,将 public 资产(。例如,方法和成员) 和初始化在构造函数的外部。 这将使对象构造快速且占用空间低。

// Goodfunction Button(text) {
 this.text = text; 
}
Button.prototype.resetText = function() { // public methodthis.text = ""; 
};
// Badfunction Button(text) {
 this.text = text; 
 this.resetText = function() { // PROBLEM: public method declared as privilagedthis.text = ""; 
 };
}

只有在构造函数访问类资产或者构造函数初始化带有提供参数的成员时,才声明构造函数内部的方法。 对于所有其他情况,使用 Prototype 添加类资产。

图- 最近的智能手机提供'桌面等级'处理器

虽然最近的智能手机提供'桌面等级'处理器,但嵌套创造了一个范围,并降低 NAME 分辨率。

类&对象- 添加或者删除资源 On-the-fly

图- JavaScript就像一个灵活的自由大小t 恤

尽管JavaScript中的类充当对象创建的'蓝色打印',但是你可以在创建对象之后添加方法和成员。 'on-the-fly'立即添加类资产'all特定的'对象实例'对象 asset 只添加到'的所有实时'该类的对象'on-the-fly。 考虑以下因素:

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: undefinedButton.prototype.resetText = function() { // on-the-fly 'class asset'this.text = ""; 
};
b1.resetText(); // OKb2.resetText(); // OK too

图- 添加类资产- 在类Car中添加立体声系统

就像你可以向类或者对象添加资产一样,你也可以将它们。

delete b1.toolTip;
Button.prototype.resetText = undefined;
b1.toolTip; // undefinedb2.resetText(); // ERROR

图- 删除对象资产- 从对象车上删除报警系统

对于在许多JavaScript文件中使用异步 MODULE 定义( AMD )的大型对象来说,fly资产管理for很有用。 对于较小的对象,组合可能对设备能力检测和'修剪'对象的必要性有用。

密封和冻结对象( 非类)

图- JavaScript提供了各种停止on-the-fly资产添加和删除的方法

当我们讨论 JavaScript。JScript和ECMAScript时,它们是 1语言或者 3语言。? 为了保护脚本的安全性,我想在这个主题中简单地触摸脚本 5来关闭对象的on-the-fly资产添加和删除。

JavaScript是一个 Netscape,现在Mozilla支持语言,而JScript是由微软开发的类似版本。 in,微软和网景都同意基于ECMA标准开发他们的语言版本,叫做 ECMA脚本 short short short short。

在,( 或者 JavaScript 1.8.5 ) 兼容浏览器中,一个可以 Seal()Freeze()。 你不能添加或者删除密封对象的属性,而 Freezing Freezing adds editable editable editable editable editable editable editable editable adds adds。 如果只希望停止添加新属性,但可以删除现有属性,请使用 preventExtensions()。 密封。冻结和防止扩展不适用于类。 考虑示例 below:

function Button(text) {
 this.text = text; 
}var b = new Button("My Button");
Object.seal( b ); 
Object.isSealed( b ); // returns trueb.text = "Submit"; // OKb.toolTip = "Mine"; // FAIL silentlydelete b.text; // FAIL silentlyButton.prototype.toolTip = "Mine"; // OK, only object is sealed not Button classObject.freeze( b ); 
Object.isFrozen( b ); // returns trueb.text = "Submit"; // FAIL silentlydelete b.text; // FAIL silentlyButton.prototype.toolTip = "Mine"; // OK, only object is freezed not Button classObject.preventExtensions( b ); 
Object.isExtensible( b ); // returns falseb.text = "Submit"; // OK, you can still editdelete b.text; // OK toob.toolTip = "Mine"; // FAIL silentlyButton.prototype.toolTip = "Mine"; // OK, only object is prevents extentions not Button class

资产的哈希特性

图- JavaScript属性类似 key-value 对

JavaScript属性可以使用"索引"符号。"密钥"符号和"圆点"概念来声明。 在不同上下文中都是有用的构造。 请参见示例 below:

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 loopsb1["is enabled"]; // return true. Mostly used for space separated, hypen'ed propertiesb1.focused; // good ol dot notation return false

这是很好的忠告呵!

图-'那个'是一种给'这个'提供嵌套函数的方法

在JavaScript中,的private 方法不能访问的privilaged资源。 以下操作将不起作用:

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

为了提供必要的访问,JavaScript社区使用了一个使用 var'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 = thisfunction isEmpty() {return that.text === "" }
 this.getText = function() { return isEmpty()? "NO TEXT" : this.text }
}var b1 = new Button("") 
b1.getText() // return"NO TEXT" 
对于返回。抛出。中断或者继续,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=0switch (day)
{case0:
 x = "Sunday" break // ASI herecase1:
 x = "Monday" breakcase2:
 x = "Tuesday" breakcase3:
 x = "Wednesday" breakcase4:
 x = "Thursday" breakcase5:
 x = "Friday" breakcase6:
 x = "Saturday" break
}
alert(x) // return"Sunday"
// ASI if line ends with 'continue'continue// ASI here continue;
以下是JavaScript将新行视为前一项的延续的地方,因这里JavaScript不会自动插入半部分:
// No ASI for unclosed parenfunction onclick(
e) 
// No ASI for unclosed array literalvar states = ["NY", "MA", "CA"] 
// No ASI for unclosed object literalvar states = {
name : "Newyork", code : "MA"} 
// No ASI if ends with commavar states,
cities 
// No ASI if ends with dotfrom("state").
where("id='MA'").
select()
// No ASI if ends with ++ or --var i = 1, j=5i
++
j// here i = 1 and j = 6
// No ASI for 'for'for(var i = 0; i <2; i++)
alert("hello") // part of loopalert("world") // not a part of loop
// No ASI for 'while'var i = 0;while(i++ <2)
alert("hello") // part of loopalert("world") // not a part of loop
// No ASI for 'do'var i = 0;doalert("hello") // part of loop, to put 2 or more statements use {}while (i++ <2)
// No ASI for 'if'var i = 0if(i == 0)
alert("hello") // alert will popup
// No ASI for 'else'var i = 1;if(i == 0)
alert("hello") elsealert("world") // alert will popup

没有类的对象

图- 没有类型的对象

JavaScript是一种在没有类的情况下创建对象的语言。 其中最简单的是'空白'对象 换句话说,其中没有任何资产。 可以随时在空对象中添加资产,也可以使用资产声明对象。 JavaScript中的{} 表示空对象或者空对象文本。

// empty objectvar b={};

现在添加一些属性:

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

然后添加一个方法:

// object with methodvar 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 objectsvar toolbar = {
 button1: {text: "My Button 1"}
, button2: {text: "My Button 2"}
}
toolbar.button2.text; // return"My Button 2"

名称空间- 避免 NAME 冲突

可以期望脚本与其他脚本一起运行,因此将代码保持为的命名空间非常重要。 目前为止,我们已经在全局范围内编写了所有类。 让我们创建几个命名空间。

// 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 declarationfunction () {}

上面 将失败为:

  • JavaScript不期望匿名函数。
  • 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 functioncom.Acme.UI.Button = (function () {}());

现在我们有了匿名'容器'来保存我们的类。 让我们 add。

// split one liner anonymous container in multi-linecom.Acme.UI.Button = (function () {
}());// add constructorcom.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 propertycom.Acme.UI.Button = (function () {
 function Button(text) {
 this.text = text;
 }
 return Button;
}());// add public, private and privileged assetscom.Acme.UI.Button = (function () {
 // constructorfunction Button(text) {
 this.text = text; // public propertyvar that = this;
 // private methodfunction isEmpty() {
 return that.text === "";
 }
 // privileged methodthis.getText = function () {
 return isEmpty()? "NO TEXT" : this.text;
 };
 }
 // public method Button.prototype.resetText = function () {
 returnthis.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库。 或者模块提供了一个范围,你可以同时拥有 public 和 private members成员。

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 library库,如图所示。

var Util = (function (u) {
 // private var key = "secret";
 // private methodfunction hash() {
 return123;
 }
 // public property u.version = "1.0";
 // public method u.productName = function () {
 return"Acme Library";
 }
 return u;
}(Util || {}));
Util.productName(); // return"Acme Library"

以下是的另一种方法:隐藏 private 状态和导出 public methods方法。 请注意,在调用匿名函数时返回一个对象。

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;
 returnthis;
 },
 add: function (v) {
 this.value += v;
 returnthis;
 },
 shout: function () {
 console.log(this.value);
 }
};// chain method callsobj.increment().add(3).shout(); // 5// As opposed to calling them one by oneobj.increment();
obj.add(3);
obj.shout();

清除getter和 setter

JavaScript通过 getset 为属性获取和设置方法提供了一个干净的语法。

var carStero = {
 _volume: 0,
 get volume() {
 returnthis._volume;
 },
 set volume(value) {
 if (value <0 || value> 100) {
 thrownew Error("Volume shold be between 0-100");
 }
 this._volume = value;
 }
};try {
 carStero.volume = 110;
} catch (err) {
 alert(err.message);
}

全局

- 最小化全局空间的使用

你可以很容易地声明全局变量:

var width = 100; // globalheight = 50; // global without vardelete width; // ERRORdelete height; // OKvar states = ["CA", "NY", "MA"];delete states[1]; // OK, delete NYstates[1]; // return undefinedstates.length; // return 3

delete 操作符移除 array 元素时,该元素不再在 array 中,并且访问它返回的是未定义的。 它仍然可以寻址。

没有 var的任何成员都将成为全局范围。

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

ECMAScript 5严格模式

为ES5提供了一个严格的模式解释。 这是他要做

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

在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

几乎任何尝试使用 NAME'eval'的尝试都是为了将 eval 函数分配给一个变量或者一个对象属性。

// All generate ERRORobj.eval=... new Function("eval") 

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

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

finally,一个长期的( 非常烦人) Bug 已经得到解决: coerced或者未定义is成为全局对象的情况。 Strict模式现在防止这种情况发生并引发异常。

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

当严格模式启用时 with(){} 语句就死了- 实际上,它甚至显示为语法错误。

函数参数

有一个 arguments 对象仅在函数 body 中可用。 这可以能对创建多个类构造函数或者实用程序函数来循环所有 arguments 来执行一些任务 比如。

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

关闭

在函数返回的情况下,函数可以在堆栈上使用闭合变量( 即使是在函数。 通常情况下,当函数返回时,它的堆栈被破坏,因此所有变量都是。 当一个典型函数返回到调用环境时,它的所有局部变量和参数都会成为垃圾 Collection的。

当函数的局部变量 函数或者函数argument在函数返回后仍保持活动状态时,就会出现闭包。 考虑以下返回匿名函数的函数。 匿名内部函数在返回时记住 min的值,即使稍后在代码中调用了内部函数。

function volumeFactory(min) {
 returnfunction (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是一种面向对象语言?

开发人员对'派生类类类'的继承概念很熟悉。 他们从教育的时代和计算机科学教科书中知道这一点。 wired Car 是一种快速的面向对象的方法,扩展了面向对象的Vehicle {} 和类 Truck的实现,它是一种快速的面向对象的方法。

当这样的开发人员在JavaScript中尝试OO时,他们意识到,除了父 child child friends friends friends friends friends。 如果你不知道怎么做,那两个朋友之间的关系就是你的帮助,如果你不知道如何去做的话 ! 在某种程度上,JavaScript基于 Prototype的链接告诉父对象是他们孩子的朋友:不过这只是一个观点。

开发者们善于在父子父子关系中思考和建模事物,并且他们是为了两个简单的原因而做的,而不是。 A ) 更好地理解代码和B 以重用代码。 你已经看到,JavaScript提供了各种方式来做这两种事情,但是有一些不同的方法。 一旦你掌握了语言,你会喜欢它的。

我只是浏览了一下JavaScript所提供的内容。 希望在本系列的后续部分中我们能看到更多的功能。

结束语

最初我想我将介绍 Backbone。Angular。to 等等的JavaScript框架,但是它似乎足以保留下一个讨论。 我们将查看这些流行的框架,看看它们解决了什么问题。


WEB  JAVA  Javascript  PAR  模式  arc  
相关文章