js的对象有一大堆细节, 然并卵. 只要记着js是基于prototype的就好了.

遍历

for...in 循环
该方法依次访问一个对象及其原型链中所有可枚举的属性
Object.keys(o)
该方法返回一个对象 o 自身包含不包括原型中的所有属性的名称的数组
Object.getOwnPropertyNames(o)
该方法返回一个数组它包含了对象 o 所有拥有的属性无论是否可枚举的名称

也可以这么做(古代方法): 
function listAllProperties(o){     
	var ob;     
	var re = [];
	
	for(ob = o; ob !== null; ob = Object.getPrototypeOf(ob)){  
		re = re.concat(Object.getOwnPropertyNames(ob));  
	}
	
	return re; 
}

集合/对象

var obj = {
    name: "Carrot",
    "for": "Max",
    details: {
        color: "orange",
        size: 12
    }
}
obj.details.color; // orange
obj["details"]["size"]; // 12

//数组
var a = ["dog", "cat", "hen"];
a.length; // 3 就这个特殊了. 否则数组就是个集合.
//遍历有四个方法
for (var i = 0; i < a.length; i++)
for (var i in a)
a.forEach(function() 
//有用的方法
push
pop
shift
          
//函数
function add() {
    var sum = 0;
    for (var i = 0, j = arguments.length; i < j; i++) {
        sum += arguments[i];
    }
    return sum;
}

add(2, 3, 4, 5); // 14
          
add.apply(null, [2, 3, 4, 5]); // 传入一个数组
//可以匿名
var a = 1;
var b = 2;
(function() {
    var b = 3;
    a += b;
})();

a; // 4
b; // 2

//递归调用, 遍历节点树.
var charsInBody = (function counter(elm) {
    if (elm.nodeType == 3) { // 文本节点
        return elm.nodeValue.length;
    }
    var count = 0;
    for (var i = 0, child; child = elm.childNodes[i]; i++) {
        count += counter(child);
    }
    return count;
})(document.body);



//非prototype方式的集合

function makePerson(first, last) {
    return {
        first: first,
        last: last,
        fullName: function() {
            return this.first + ' ' + this.last;
        },
        fullNameReversed: function() {
            return this.last + ', ' + this.first;
        }
    }
}
s = makePerson("Simon", "Willison");
s.fullName(); // Simon Willison
s.fullNameReversed(); // Willison, Simon

//function放到prototype总是更好的方法
function Person(first, last) {
    this.first = first;
    this.last = last;
}
Person.prototype.fullName = function() {
    return this.first + ' ' + this.last;
}
Person.prototype.fullNameReversed = function() {
    return this.last + ', ' + this.first;
}

//prototype可以后找补.
s = new Person("Simon", "Willison");
s.firstNameCaps();  // TypeError on line 1: s.firstNameCaps is not a function

Person.prototype.firstNameCaps = function() {
    return this.first.toUpperCase()
}
s.firstNameCaps(); // SIMON


//apply的说明

function trivialNew(constructor, ...args) {
    var o = {}; // 创建一个对象
    constructor.apply(o, args);//apply的第一个参数就是this的作用域
    return o;
}
var bill = trivialNew(Person, "William", "Orange");
var bill = new Person("William", "Orange");
//上面这两句是等价的.


lastNameCaps.call(bill); //假设lastNameCaps这个函数存在.
// 和以下方式等价
bill.lastNameCaps = lastNameCaps;
bill.lastNameCaps();


//闭包
function makeAdder(a) {
    return function(b) {
        return a + b;
    }
}
var x = makeAdder(5);
var y = makeAdder(20);
x(6); // 11
y(7); // 27


//内存泄漏
function addHandler() {
    var el = document.getElementById('el');
    el.onclick = function() {
        el.style.backgroundColor = 'red';//这个el很难被释放.
    }
}
function addHandler(){
    document.getElementById('el').onclick = function(){
        this.style.backgroundColor = 'red';//这样就解决了内存泄漏.
    };
}
//避免使用过多的闭包,可以用let关键词

function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
}
MyObject.prototype.getName = function() {
  return this.name;
};
MyObject.prototype.getMessage = function() {
  return this.message;
};
//下面是简洁写法, 但是括号增加了, 其实也难说就好了.

function MyObject(name, message) {
    this.name = name.toString();
    this.message = message.toString();
}
(function() {
    this.getName = function() {
        return this.name;
    };
    this.getMessage = function() {
        return this.message;
    };
}).call(MyObject.prototype)

几乎照抄: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/A_re-introduction_to_JavaScript

迭代器

//这个我写过......
function* idMaker() { //*星号的意思是, 自动帮你维护迭代状态.
  var index = 0;
  while(true)
    yield index++;// yield是关键, 当这个迭代器的 next() 方法被首次(后续)调用时,其内的语句会执行到第一个(后续)出现yield的位置为止,yield 后紧跟迭代器要返回的值。或者如果用的是 yield*(多了个星号),则表示将执行权移交给另一个生成器函数(当前生成器暂停执行)。
}

var gen = idMaker();

console.log(gen.next().value); // 0
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2

// 不用星*的写法:

function makeIterator(array){
    var nextIndex = 0;

    return {
       next: function(){
           return nextIndex < array.length ?
               {value: array[nextIndex++], done: false} :
               {done: true};
       }
    };
}

var it = makeIterator(['yo', 'ya']);
console.log(it.next().value); // 'yo'
console.log(it.next().value); // 'ya'
console.log(it.next().done);  // true

迭代器的实现

为了实现可迭代,一个对象必须实现 **@@iterator **方法,这意味着这个对象(或其原型链中的一个对象)必须具有带 Symbol.iterator 键的属性:

var myIterable = {};
myIterable[Symbol.iterator] = function* () {
    yield 1;
    yield 2;
    yield 3;
};

for (let value of myIterable) { 
    console.log(value); 
}
// 1
// 2
// 3

or

[...myIterable]; // [1, 2, 3]

参考: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Iterators_and_Generators

元编程

proxy和reflect

let handler = {
  get: function(target, name){return name in target ? target[name] : 42}
}

let p = new Proxy({}, handler);//proxy是一个空对象, get属性用handle
p.a = 1;

console.log(p.a, p.b); // 1, 42

参考: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Meta_programming

内存

//循环引用会导致引用计数失灵.

function f(){
  var o = {};
  var o2 = {};
  o.a = o2; // o 引用 o2
  o2.a = o; // o2 引用 o

  return "azerty";
}

f();

//实际例子:
var div;
window.onload = function(){
  div = document.getElementById("myDivElement");
  div.circularReference = div;//这里自己引用了自己
  div.lotsOfData = new Array(10000).join("*");
};
//但是现代浏览器是根引用判定, 从根部开始遍历, 如果有某个对象在整个引用树中都没有引用, 则这个对象可以回收.

矢量运算simd

SIMD.%type%.select() 
SIMD.%type%.bool()

缓冲/写屏(视图)

ArrayBuffer 是一种数据类型用来表示一个通用的固定长度的二进制数据缓冲区你不能直接操纵一个ArrayBuffer中的内容你需要创建一个类型化数组的视图或一个描述缓冲数据格式的DataView使用它们来读写缓冲区中的内容
 

原型链

在原型链上查找属性比较耗时,对性能有副作用

当谈到继承时JavaScript 只有一种结构对象每个对象都有一个私有属性称之为 [[Prototype]]),它持有一个连接到另一个称为其 prototype 对象原型对象的链接 prototype 对象又具有一个自己的原型层层向上直到一个对象的原型为 null
译者注Object.getPrototypeOf(Object.prototype) === null; // true)
根据定义null 没有原型并作为这个原型链中的最后一个环节

JavaScript 中几乎所有的对象都是位于原型链顶端的Object的实例


var o = new Foo();
//JavaScript 实际上执行的是:

var o = new Object();
o.__proto__ = Foo.prototype;
Foo.call(o);
//然后当你真的做某个调用的时候, js就会在原型链上查找.
//(可以通过Object.getPrototypeOf(obj)或者已被弃用的__proto__属性获得)
//没有官方的方法用于直接访问一个对象的原型对象——原型链中的“连接”被定义在一个内部属性中,在 JavaScript 语言标准中用 [[prototype]] 表示(参见 ECMAScript)。然而,大多数现代浏览器还是提供了一个名为 __proto__ (前后各有2个下划线)的属性,其包含了对象的原型。你可以尝试输入 person1.__proto__ 和 person1.__proto__.__proto__,看看代码中的原型链是什么样的!
1. 那些定义在构造器函数中的用于给予对象实例的这些都很容易发现 - 在您自己的代码中它们是构造函数中使用this.x = x类型的行在内置的浏览器代码中它们是可用于对象实例的成员通常通过使用new关键字调用构造函数来创建例如var myInstance = new myConstructor())。
2. 那些直接在构造函数上定义仅在构造函数上可用的这些通常仅在内置的浏览器对象中可用并通过被直接链接到构造函数而不是实例来识别 例如Object.keys()
3. 那些在构造函数原型上定义由所有实例和对象类继承的这些包括在构造函数的原型属性上定义的任何成员如myConstructor.prototype.x()

json

JSON.parse(myJSON): 以文本字符串形式接受JSON对象作为参数并返回相应的对象。。 
JSON.stringify(myJSON);: 接收一个对象作为参数返回一个对应的JSON字符串
//不论是log对象还是alert对象, 用上面这句话就对了.

对象的打印

JSON.stringify(myJSON)// 此方法ok, 也是唯一可用的方法
JSON.stringify(YOUR_OBJECT_HERE, null, 4)//The second argument alters the contents of the string before returning it. The third argument specifies how many spaces to use as white space for readability.
object.toSource() //不行, 只能firefox
value.toString() //明确的说这种方法不行, 他仅仅返回一个"[Object]"
"" + value //明确的说这种方法不行, 他仅仅返回一个"[Object]"
String(value) //明确的说这种方法不行, 他仅仅返回一个"[Object]"

性能问题

  • 闭包有
  • 原型链有
  • settimeout这种异步也有性能损耗
  • 循环引用, 至少给内存回收添了一点小麻烦.