Javascript集合

对于noobie问题抱歉。 你能解释一下,有什么区别:

1. var a = []; a['b'] = 1; 2. var a = {}; a['b'] = 1; 

我找不到在互联网上的文章,所以写在这里。

字面

[]{}分别被称为数组和对象文字。

var x = []var x = new Array();

var y = {}var y = new Object();简写var y = new Object();

数组

数组是具有长度属性的结构。 您可以通过数字索引访问值。

 var x = [] or var x = new Array(); x[0] = 'b'; x[1] = 'c'; 

如果你想列出你所做的所有属性:

 for(var i = 0; i < x.length; i++) console.log(x[i]);// numeric index based access. 

性能技巧和陷阱

1.内部caching长度属性

标准数组迭代:

 for (var i = 0; i < arr.length; i++) { // do stuff }; 

鲜为人知的事实:在上面的场景中,在for循环的每一步都读取arr.length属性。 就像你在那里调用的任何函数一样:

 for (var i = 0; i < getStopIndex(); i++) { // do stuff }; 

这无缘无故地降低了性能。 内部caching来拯救:

 for (var i = 0, len = arr.length; i < len; i++) { // enter code here }; 

这是以上证据。

2.不要在构造函数中指定数组长度。

 // doing this: var a = new Array(100); // is very pointless in JS. It will result in an array with 100 undefined values. // not even this: var a = new Array(); // is the best way. var a = []; // using the array literal is the fastest and easiest way to do things. 

数组定义的testing用例可以在这里find

3.避免使用Array.prototype.push(arr.push)

如果你正在处理大集合,直接分配比使用Array.prototype.push();更快Array.prototype.push(); 方法。

myArray[i] = 0;myArray.push(0);更快myArray.push(0); 根据jsPerf.com testing用例 。

4.将数组用于关联赋值是错误的。

它工作的唯一原因是因为Array扩展了JS语言核心内的Object类。 你可以使用Date();RegEx(); 对象。 这不会有所作为。 x['property'] = someValue 必须总是和Objects一起使用。

数组应该只有数字索引。 看到这个 ,Google的JS开发指南! 避免for (x in arr)循环或arr['key'] = 5;

这可以很容易地备份,看这里的例子。

 var x = []; console.log(x.prototype.toString.call); 

会输出: [object Array]

这揭示了核心语言的“类”inheritance模式。

 var x = new String(); console.log(x.prototype.toString.call); 

会输出[object String]

5.从数组中获取最小值和最大值。

一个鲜为人知的,但真正强大的把戏:

 function arrayMax(arr) { return Math.max.apply(Math, arr); }; 

, 分别:

 function arrayMin(arr) { return Math.min.apply(Math, arr); }; 

对象

有了一个对象,你只能做到:

var y = {}var y = new Object();

y['first'] = 'firstValue'y.first = 'firstValue' ,这是数组不能做到的。 对象devise用于使用String键进行关联访问。

迭代是这样的:

 for (var property in y) { if (y.hasOwnProperty(property)) { console.log(y.property); }; }; 

性能技巧和陷阱

1.检查一个对象是否有属性。

大多数人使用Object.prototype.hasOwnProperty 。 不幸的是,这往往会导致错误的结果,导致意想不到的错误。

这是一个很好的方法来做到这一点:

 function containsKey(obj, key) { return typeof obj[key] !== 'undefined'; }; 

2.更换开关语句。

其中一个简单而有效的JS技巧是switchreplace。

 switch (someVar) { case 'a': doSomething(); break; case 'b': doSomethingElse(); break; default: doMagic(); break; }; 

在大多数JS引擎上面是痛苦地缓慢。 当你看到三个可能的结果时,它没有什么区别,但是如果你有几十个或几百个呢?

以上可以很容易地用一个对象来代替。 不要添加尾随() ,这不是执行函数,而是简单地存储对它们的引用:

 var cases = { 'a': doSomething, 'b': doSomethingElse, 'c': doMagic }; 

而不是switch

 var x = ???; if (containsKey(cases, x)) { c(x); } else { console.log("I don't know what to do!"); }; 

3.深入克隆变得容易。

 function cloneObject(obj) { var tmp = {}; for (var key in obj) { tmp[key] = fastDeepClone(obj[key]; }; return tmp; } function cloneArr(arr) { var tmp = []; for (var i = 0, len = arr.length; i < len; i++) { tmp[i] = fastDeepClone(arr[i]); } return tmp; } function deepClone(obj) { return JSON.parse(JSON.stringify(obj)); }; function isArray(obj) { return obj instanceof Array; } function isObject(obj) { var type = typeof obj; return type === 'object' && obj !== null || type === 'function'; } function fastDeepClone(obj) { if (isArray(obj)) { return cloneArr(obj); } else if (isObject(obj)) { return cloneObject(obj); } else { return obj; }; }; 

这里是深层克隆function的行动。

自动装箱

作为一种dynamictypes的语言,JavaScript在本地对象types方面是有限的:

  • 目的
  • 排列
  • 布尔
  • date
  • 正则expression式
  • 错误

Null不是一个types, typeof null是对象。

有什么收获? 原始和非原始对象之间有很强的区别。

 var s = "str"; var s2 = new String("str"); 

他们做同样的事情,你可以调用ss2s所有string方法。 然而:

 type of s == "string"; // raw data type type of s2 == "object" // auto-boxed to non-primitive wrapper type s2.prototype.toString.call == "[object String]"; 

你可能会听到在JS中一切都是一个object 。 这不完全正确,虽然这是一个非常容易犯的错误。

实际上有2种types,基本types和对象,当你调用s.indexOf("c") ,JS引擎会自动将s转换为非基元包装types,在这种情况下是object String ,其中所有的方法都是在String.prototype上定义。

这被称为auto-boxingObject.prototype.valueOf(obj)方法是强制从基元转换为非基元的一种方法。 像Java这样的语言为它自己的许多原语(特别是对)引入了相同的行为: int – Integer, double – Double, float – Float等

你为什么要在意?

简单:

 function isString(obj) { return typeof obj === "string"; } isString(s); // true isString(s2); // false 

因此,如果s2是用var s2 = new String("test")创build的,那么即使对于另外可以想象的简单types检查,也会得到一个错误的否定结果。 更复杂的物体也会带来沉重的性能损失。

像某些人会说的微观优化,但是结果是非常显着的,即使对于诸如string初始化之类的非常简单的事情也是如此。 我们来比较下面两个方面的performance:

 var s1 = "this_is_a_test" 

 var s2 = new String("this_is_a_test") 

您可能会期望全面匹配性能,但是令人惊讶的是,使用new String的后者声明比第一个new String慢了92%,如这里所certificate的。

function

1.默认参数

|| 运营商是最简单的违约方式。 为什么它工作? 由于真理和谬误的价值。

在逻辑条件下评估时, undefined值和null值将自动转化为false

一个简单的例子(代码在这里 ):

 function test(x) { var param = x || 5; // do stuff with x }; 

2. OO JS

要理解的最重要的事情是JavaScript this对象是不可改变的。 这只是一个可以很容易地改变的参考。

在OO JS中,我们依靠new关键字来保证JS类的所有成员的隐式范围。 即便如此,您可以通过Function.prototype.callFunction.prototype.apply轻松更改范围。

另一个非常重要的是Object.prototype 。 嵌套在对象原型上的非原始值是共享的,而原始的不是。

代码在这里的例子。

一个简单的类定义:

 function Size(width, height) { this.width = width; this.height = height; }; 

一个简单的大小类,有两个成员this.widththis.height

在类定义中,不pipe前面有什么,都会为每个Size的实例创build一个新的引用。

为类添加方法,为什么“封闭”模式和其他“花式名称模式”是纯虚构的

这也许是最恶毒的JavaScript反模式的地方。

我们可以通过两种方式在我们的Size类中添加一个方法。

 Size.prototype.area = function() { return this.width * this.height; }; 

要么:

 function Size2(width, height) { this.width = width; this.height = height; this.area = function() { return this.width * this.height; } } var s = new Size(5, 10); var s2 = new Size2(5, 10); var s3 = new Size2(5, 10); var s4 = new Size(5, 10); // Looks identical, but lets use the reference equality operator to test things: s2.area === s3.area // false s.area === s4.area // true 

Size2area方法是为每个实例创build的。 这是完全无用和缓慢的,很慢很多。 89%是确切的。 看这里

上述说法对于所有已知的“花式名称模式”的约99%是有效的。 记住JS中最重要的东西,那些东西只不过是小说。

有很强的架构参数可以做,主要围绕数据封装和闭包的使用。

不幸的是,这样的事情在JavaScript中毫无价值,性能损失根本就不值得。 我们正在谈论90%以上,这是什么,但可以忽略不计。

3.限制

由于prototype定义是在类的所有实例之间共享的,因此您将无法在其中放置非原始设置对象。

 Size.prototype.settings = {}; 

为什么? size.settings对每个实例都是一样的。 那么基元是什么呢?

 Size.prototype.x = 5; // won't be shared, because it's a primitive. // see auto-boxing above for primitive vs non-primitive // if you come from the Java world, it's the same as int and Integer. 

重点:

平均JS人会用以下方式写JS:

 var x = { doStuff: function(x) { }, doMoreStuff: 5, someConstant: 10 } 

这是好的 (罚款=质量差,难以维护的代码),只要你明白,这是一个Singleton对象,这些函数只能在全局范围内使用,而不需要引用它们。

但是,它会得到绝对可怕的代码:

 var x = { width: 10, height: 5 } var y = { width: 15, height: 10 } 

你可以得到: var x = new Size(10, 5); var y = new Size(15, 5); var x = new Size(10, 5); var y = new Size(15, 5);

input时间更长,您需要每次input相同的内容。 再一次,这是一个很大的优势。 看这里

整个标准不好

这几乎可以在任何地方看到:

  function() { // some computation var x = 10 / 2; var y = 5; return { width: x, height: y } } 

再次与替代:

 function() { var x = 10 / 2; var y = 5; return new Size(10, 5); }; 

重点:使用任何适当的类!

为什么? 例1是93%较慢 。 看这里 这里的例子是微不足道的,但是它们说明了JS,OO中被忽略的东西。

不要雇用那些认为JS没有课程的人,并且从招聘者那里find“面向对象”JS的工作。

closures

很多人都喜欢他们,因为它给了他们一种数据封装的感觉。 除了大幅度的90%的性能下降之外,这里同样容易忽视。 内存泄漏。

 function Thing(someParam) { this.someFn = function() { return someParam; } } 

你刚刚为someParam创build了一个闭包。 为什么这不好? 首先,它迫使你将类方法定义为实例属性,导致性能下降。

其次,它会消耗内存,因为封闭永远不会被解除引用。 在这里寻找证据。 当然,你会得到一些伪造的数据封装,但是你使用了三倍的内存,性能下降了90%。

或者你可以添加@private并获得一个下划线函数名称的方式。

其他生成封闭的常见方法是:

 function bindSomething(param) { someDomElement.addEventListener("click", function() { if (param) //do something else // do something else }, false); } 

param现在是closures了! 你如何摆脱它? 有各种各样的技巧,有些发现在这里 。 最好的办法,尽pipe更严格的是避免使用匿名函数,但这需要一种方法来指定事件callback的范围。

就我所知,这种机制仅在Google Closure中可用。

单身模式

好的,那么我该怎么做单身? 我不想存储随机引用。 这是从Google Closure的base.js中无耻地窃取的一个奇妙的想法

 /** * Adds a {@code getInstance} static method that always return the same instance * object. * @param {!Function} ctor The constructor for the class to add the static * method to. */ function addSingletonGetter(ctor) { ctor.getInstance = function() { if (ctor.instance_) { return ctor.instance_; } return ctor.instance_ = new ctor; }; }; 

这是Java的,但它是一个简单而强大的技巧。 你现在可以做:

 project.some.namespace.StateManager = function() { this.x_ = 5; }; project.some.namespace.prototype.getX = function() { return x; } addSingletonGetter(project.some.namespace.StateManager); 

这有用吗? 简单。 在所有其他文件中,每次需要引用project.some.namespace.StateManager ,都可以编写: project.some.namespace.StateManager.getInstance() 。 这比看起来更棒。

您可以拥有全局状态,具有类定义(inheritance,有状态成员等)的优点,并且不会污染全局名称空间。

单实例模式

你现在可能会这样做:

 function Thing() { this.someMethod = function() {..} } // and then use it like this: Thing.someMethod(); 

这是JavaScript中另一个不容忽视的问题。 请记住,只有在使用new关键字时,才能保证this对象是不可变的。 上述代码背后的魔力很有趣。 this实际上是全局范围,所以没有意义给你添加全局对象的方法。 你猜对了,那些东西永远不会被垃圾收集。

没有什么可以告诉JavaScript来使用别的东西。 它自己的函数没有范围。 要小心你用static属性做什么。 为了重现一次我读过的引用,JavaScript全局对象就像是一个公共厕所。 有时你别无select,只能去那里,尽量减less与表面的接触。

要么坚持上面的Singleton模式,要么使用嵌套在命名空间下的设置对象。

在JavaScript中的垃圾收集

JavaScript是一种垃圾收集的语言,但JavaScript GC通常相当不甚了解。 重点又是速度。 这也许是太熟悉了。

 // This is the top of a JavaScript file. var a = 5; var b = 20; var x = {};//blabla // more code function someFn() {..} 

这是糟糕的,糟糕的性能代码。 原因很简单。 JS将垃圾回收一个variables,并释放它所拥有的堆内存,只有当该variables被取消范围,例如没有任何内存的引用。

例如:

 function test(someArgs) { var someMoreStuf = // a very big complex object; } test(); 

三件事:

  • 函数参数被转换成本地定义
  • 内部声明被悬挂 。
  • 当函数完成执行时,为内部variables分配的所有堆内存都被释放。

为什么? 因为他们不再属于“当前”范围。 他们被创造,使用和销毁。 也没有closures,所以你使用的所有内存都通过垃圾收集来释放。

出于这个原因,你永远不应该,你的JS文件不应该看起来像这样,因为全球范围将只是保持污染内存。

 var x = 5; var y = {..}; //etc; 

好吧,现在呢?

命名空间

JS没有名称空间,所以这不完全是一个Java的等价物,但从代码库pipe理的angular度来看,你得到你想要的。

 var myProject = {}; myProject.settings = {}; myProject.controllers = {}; myProject.controlls.MainController = function() { // some class definition here } 

美丽。 一个全局variables。 正确的项目结构。 通过构build阶段,您可以跨项目拆分项目,并获得适当的开发环境。

从这里可以达到的目标是没有限制的。

数你的图书馆

有无数的代码库的工作的乐趣,最后也是最重要的论据是要非常注意你的代码依赖。 我已经看到程序员随便把jQuery添加到堆栈的混合中,以获得简单的animation效果等等。

依赖和包pipe理是JavaScript世界最长时间没有解决的问题,直到创build像Bower这样的工具。 浏览器仍然有点慢,即使速度很快,互联网连接速度也很慢。

在Google这个世界里,他们经历了编写整个编译器的篇幅, 只是为了节省字节 ,而这种方法在很多方面都是Web编程中的正确心态。 我非常重视Google,因为他们的JS库提供了像Google Maps这样的应用程序,它不仅非常复杂,而且在任何地方都可以工作。

可以说,JavaScript有很多种可用的工具,因为它的普及性,可访问性,以及整个生态系统愿意接受的某种程度上的低质量。

对于黑客新闻订阅者来说,一天不会没有新的JS库,而且它们当然是有用的,但是不能忽视这样一个事实,即许多人重新实现完全相同的关注,没有任何强烈的新颖性概念或任何杀手的想法和改进。

在他们有足够时间certificate它们对整个生态系统的新颖性和实用性,以及强烈区分周日编码乐趣和生产部署之前,抵制所有新玩具混合的冲动是一个强有力的经验法则。

如果你的<head></head>标签比这篇文章长,那么你就错了。

testing你的JavaScript知识

几个“完美主义者”水平testing:

对象的集合? 使用这个符号(JavaScript数组):

 var collection = [ {name:"object 1"} , {name:"object 2"} , {name:"object 3"} ]; 

把一个新元素放到你的collections中:

 collection.push( {name:"object 4"} ); 

在JavaScript中,所有对象都是关联数组 。 在第一种情况下,你在第二种情况下创build一个数组,你创build一个空的对象,这也是数组:)。

所以在JS中,你可以像使用数组一样使用任何对象:

 var a = {}; a["temp"] = "test"; 

而作为对象:

 var a = {}; a.temp = "test"; 

我会使用一个对象数组:

 collection = [ { "key":"first key", "value":"first value" }, { "key":"second key", "value":"second value" } ]; 

等等

1)是一个数组2)是一个对象

与数组一样,其他语言也是如此

与对象也。 – 你可以得到值ab == 1 – 但在JS中,你也可以用这样的语法a [“b”] == 1来获得值

  • 这可能是有用的,当钥匙看起来像这个“一些钥匙”,在这种情况下,你不能使用“链”
  • 如果key是variables,这也是有用的

你可以这样写

  function some(f){ var Object = {name: "Boo", age: "foo"}, key; if(f == true){ key = "name"; }else{ key = "age"; } return Object[key]; } 

但我想用它作为收集,我必须select?

这取决于你想要存储的数据