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技巧是switch
replace。
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");
他们做同样的事情,你可以调用s
和s2
上s
所有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-boxing
。 Object.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.call
和Function.prototype.apply
轻松更改范围。
另一个非常重要的是Object.prototype
。 嵌套在对象原型上的非原始值是共享的,而原始的不是。
代码在这里的例子。
一个简单的类定义:
function Size(width, height) { this.width = width; this.height = height; };
一个简单的大小类,有两个成员this.width
和this.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
Size2
的area
方法是为每个实例创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?
这取决于你想要存储的数据