经典JS代码

继承

当你创建一个新的对象时,你可以选择一个对象的原型,如下所示:

1
2
3
4
5
6
7
8
if(typeof Object.create !== 'function') {
Object.create = function(o) {
var F = function(){};
F.prototype = o;
return new F();
}
}
Object.create 就是将对象作为原型生成一个新的对象。

For…In

for…in 循环会遍历对象中所有的包括原型链上的属性。

1
2
3
4
5
6
7
8
9
function a(){}
a.prototype.b = 1;
a.prototype.c = 2;
var p = new a();
p.q = 3;
for (var name in p){
console.log(name); // q, b, c
}

delete

delete只会删除当前对象中的属性,不会查找到prototype

1
2
3
4
5
6
7
8
9
function a(){}
a.prototype.b = 1;
a.prototype.c = 2;
var p = new a();
p.b =3;
console.log(p.b); // 3
delete p.b;
console.log(p.b); // 1

Function Objects

函数创建时,函数自身作为一个对象,proto关联到Function的prototype,同时创建自身的prototype,其中包括constructor,指向自身(可以看到其proto是个Function的prototype)

When a function object is created, the Function constructor that produces the func- tion object runs some code like this:

1
this.prototype = {constructor: this};

1
2
function a(){}
a.__proto__ === Function.prototype // true

,另外同时创建两个隐藏属性:函数的运行环境Scope,以及函数体里的代码。

The function object created by a function literal contains a link to that outer context. This is called closure.

1
2
3
4
5
6
7
8
9
10
11
function a(){
console.log(b); // 创建时b就关联到了外层上下文的b
}
function c(){
var b = 3;
a();
}
var b = 1;
c();
console.log( a.prototype.constructor === a) //true

Function Invocation

函数在调用时,函数会动态的收到两个额外的参数,this和arguments。在不同的调用场景下,this的赋值是不同的!

作为对象方法调用(Method)

method使用this来访问对象中的属性,this在调用时绑定。

1
2
3
4
5
6
7
8
9
var myObject = {
value: 1,
increment: function(inc) {
this.value += typeof inc === 'number' ? inc : 1;
}
}
myObject.increment();
console.log(myObject.value);

作为函数调用(Function)

当函数这样调用时,this绑定在全局对象上。

inner function does not share the method’s access to the object as its this is bound to the wrong value

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var obj = {
a: function(){
function b(){
console.log(this);
}
b();
}
}
obj.a(); // 还是会输出window对象
// 解决方案
obj.a = function(){
var that = this;
function b(){
console.log(that);
}
b();
}

作为Constructor调用

作为构造器调用时,this在运行时赋值为新建的对象,该对象的原型proto是由原构造器的prototype中的方法与属性,加上一个额外constructor属性指向构造器本身

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
function a(str){
this.str = str;
}
a.prototype.read = function(){
console.log(this.str);
}
var b = new a();
// 如果构造器原型中有constructor,这个属性是可以被替代的!!!
function a(str){
this.str = str;
}
a.prototype.constructor = function(){
console.log(this.str);
}
var c = new a();
b.__proto__.constructor === a // false
b.__proto__.constructor === a.prototype.constructor // true
// 如果构造器返回一个对象,那么就只有这个对象,没有prototype
function a(str){
return {
str: str,
}
}
a.prototype.read = function(){
console.log(this.str);
}
var b = new a();
b.read() // b.read is not a function

函数作为构造器调用的缺点在于如果你忘了加new关键字,那么,this将指向global,但是这并不会报错,这是语言设计中的缺陷。那么解决方法一个就是构造器函数的名字必须大写开头,另一个就是完全不用new。

使用Function原型中的方法来调用

在调用时主动声明this与parameter被赋予的值

1
2
3
4
5
6
7
8
9
10
11
function add(){
return this.a + this.b;
}
var obj = {
a: 2,
b: 3,
}
var c = add.apply(obj)
console.log(c);

闭包

函数中声明了一个函数,那这个函数通过scope chain就可以访问到外层函数的变量,叫做闭包。

A more interesting case is when the inner function has a longer lifetime than its outer function.
当内部的函数比外部函数的生命周期更长时,闭包就变得有趣了。

1
2
3
4
5
6
7
8
9
10
11
12
13
var fade = function (node) {
var level = 1;
var step = function () {
var hex = level.toString(16);
node.style.backgroundColor = '#FFFF' + hex + hex;
if (level < 15) {
level += 1;
setTimeout(step, 100);
}
};
setTimeout(step, 100);
};
fade(document.body);

Inner function has access to the actual variables of the outer functions and not copies。

1
2
3
4
5
6
7
8
var add_the_handlers = function (nodes) {
var i;
for (i = 0; i < nodes.length; i += 1) {
nodes[i].onclick = function (e) {
alert(i);
};
}
};

但是作为参数传入的基本类型变量就是一个copy了,

1
2
3
4
5
6
7
8
9
10
var add_the_handlers = function (nodes) {
var i;
for (i = 0; i < nodes.length; i += 1) {
nodes[i].onclick = function(i){
return function (e) {
alert(i);
}
}(i);
}
};

那如果是引用类型呢?那当然就没用啦,参数传导引用类型也是作为一个引用来传递。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var add_the_handlers = function (nodes) {
var i;
var obj = {
a:2,
b:3,
}
for (i = 0; i < nodes.length; i += 1) {
nodes[i].onclick = function(obj){
return function (e) {
alert(obj.a);
}
}(obj);
obj.a += 1;
}
};

Cascade

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
getElement('myBoxDiv').
move(350, 150).
width(100).
height(100).
color('red').
border('10px outset').
padding('4px').
appendText("Please stand by").
on('mousedown', function (m) {
this.startDrag(m, this.getNinth(m));
}).
on('mousemove', 'drag').
on('mouseup', 'stopDrag').
later(2000, function () {
this.
color('yellow').
setHTML("What hath God wraught?").
slide(400, 40, 200, 200);
}).
tip('This box is resizeable');

Curry

就是为了分次定义参数,返回最终的函数调用入口

1
2
3
4
5
6
7
8
Function.method('curry', function(){
var slice = Array.prototype.slice,
args = arguments,
that = this;
return function() {
return that.apply(null, args.concat(slice.apply(arguments)));
};
})

Memoization

Functions can use objects to remember the results of previous operations, making it possible to avoid unnecessary work. This optimization is called memoization.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var fibonacci = function(n) {
return n < 2 ? n : fibonacci(n-1) + fibonacci(n-2);
}
var fibonacci = function() {
var memo = [0, 1];
var fib = function(n) {
var result = memo[n];
if(typeof result !== 'number') {
result = fib(n-1) + fib(n-2);
memo[n] = result;
}
return result;
}
return fib;
}

继承三种方式

The instanceof operator tests presence of constructor.prototype in object’s prototype chain.

[Object] instanceof [Function]

伪类继承(Pseudoclassical)

1
2
3
4
5
6
function a() {}
function b() {}
b.prototype = Object.create(a.prototype);
b.prototype = new a();
b instanceof a; //true

原型继承(Prototypal)

1
2
3
4
5
6
7
8
9
var a = {
a1:2,
a2:3,
getA1() {return this.a1;}
}
var b = Object.create(a);
b.a1 = 4;
b.getA1();

函数继承(Functional)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
function superior(obj, name){
var superfunc = obj[name];
return function() {
return superfunc.apply(obj, arguments);
}
}
var a = function(){
var private_a = 1;
var that = {};
var getPrivateA = function(){return private_a;}
that.getPrivateA = getPrivateA;
return that;
}
var b = function(){
var that = a();
var private_b = 4;
var getSuperiorA = superior(that, getPrivateA);
var getPrivateC = function() {
return that.getPrivateA() + private_b;
}
var getPrivateA = function() {
return that.getSuperiorA() + 'from parent';
}
that.getPrivateC = getPrivateC;
that.getPrivateA = getPrivateA;
return that;
}

Make Any Object reactable

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
var makeReactable = function(that){
var registry = {}
that.fire = function(event, parameters) {
var handlers,
params,
func,
type = typeof event === 'string' ?
event: event.type
if(registry.hasOwnProperty(type)){
handlers = registry[type];
handlers.forEach((handler) => {
params = handler.parameters;
func = handler.method;
if(parameters) {
params = params.concat(parameters);
}
if(typeof func === 'string'){
func = that[func];
}
func.apply(that, params)
})
}
return this;
}
that.on = function(type, method, parameters) {
var handler = {
method: method,
parameters: parameters || [],
}
if(registry.hasOwnProperty(type)){
registry[type].push(handler)
}else{
registry[type] = [handler];
}
return this;
}
}
var obj = {};
makeReactable(obj);
Object.defineProperty(obj, 'a', {
set: function(val){
if(val !== this.value){
this.fire('changed', [{param:'a', from: this.value, to: val}]);
this.value = val;
}
},
get: function() {
return this.value
}
});
obj.on('changed', function(msg){
console.log(msg);
});
obj.a = 4;

判断是否为一个数组

1
2
3
4
5
6
7
8
var is_array = function(value){
return value &&
typeof value === 'object' &&
// value.constructor === Array 如果数组是在另一个window对象下面的Array创建的就会失效
typeof value.length === 'number' &&
typeof value.splice === 'function' &&
!(value.propertyIsEnumerable('length'));
}

深克隆

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Object.prototype.clone = function(){
function walk(prop){
if(typeof prop === 'object' && prop !== null){
if(Array.isArray(prop)){
return prop.map(item => (walk(item)));
}else{
const obj = {};
return Object.keys(prop).reduce((accu, key) => {
accu[key] = walk(prop[key]);
return accu;
}, obj);
}
}else{
return prop;
}
}
return walk(this);
}

Date Format

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Date.prototype.format = function(form){
const year = 'yyyy';
const month = 'mm';
const day = 'dd';
const hour = 'hh';
const minute = 'mm';
const second = 'ss';
return form.replace(year, this.getFullYear())
.replace(month, this.getMonth())
.replace(day, this.getUTCDay())
.replace(hour, this.getHours())
.replace(minute, this.getMinutes())
.replace(second, this.getSeconds());
}