Skip to content

Instantly share code, notes, and snippets.

@natebass
Forked from daocren/Javascript.md
Last active August 31, 2015 19:52
Show Gist options
  • Save natebass/250f2d291c2913827e24 to your computer and use it in GitHub Desktop.
Save natebass/250f2d291c2913827e24 to your computer and use it in GitHub Desktop.
javascript

Javascript

Objects 对象

  • Simple Type is immutable
    Number,String,boolean,null,undefined
  • Object is mutable

Object are mutable keyed collections
property can be any value except undefined
retrieve proptery: obj[name] equals to obl.name

The undefined value is produced if an attempt is made to retrieve a nonexistent member:

stooge["middle-name"] // undefined

The || operator can be used to fill in default values:

var middle = stooge["middle-name"] || "(none)";

Attempting to retrieve values from undefined will throw aTypeError exception.
This can be guarded against with the&& operator:

flight.equipment // undefined flight.equipment.model // throw "TypeError" flight.equipment && flight.equipment.model // undefined

Objects are passed around by reference.

Prototype 原型

Every object is linked to a prototype object from which it can inherit properties.
All objects created from object literals are linked to Object.prototype.
The prototype link has no effect on updating.

When we make changes to an object,the object’s prototype is not touched

The prototype link is used only in retrieval.

If we try to retrieve a property value from an object, and if the object lacks the property name, then JavaScript attempts to retrieve the property value from the prototype object. And if that object is lacking the property, then it goes to its prototype, and so on until the process finally bottoms out with Object.prototype. If the desired property exists nowhere in the prototype chain, then the result is the undefined value. This is called delegation.

The typeof operator can be very helpful in determining the type of a property:

typeof flight.number // 'number' typeof flight.status // 'string' typeof flight.arrival // 'object' typeof flight.manifest // 'undefined'

Some care must be taken because any property on the prototype chain can produce a value:

typeof flight.toString // 'function' typeof flight.constructor // 'function'

The hasOwnProperty method does not look at the prototype chain:

flight.hasOwnProperty('number') // true flight.hasOwnProperty('constructor') // false

Enumeration 枚举 The for in statement can loop over all of the property names in an object.
The most common filters are the hasOwnProperty method and using typeof to exclude functions:

var name; for (name in another_stooge) { if (typeof another_stooge[name] !== 'function') { document.writeln(name + ': ' + another_stooge[name]); } }
There is no guarantee on the order of the names

Functions 函数

The best thing about JavaScript is its implementation of functions.
Functions are the fundamental modular unit of JavaScript.
every function receives two additional parameters: this and arguments.
Because of a design error, arguments is not really an array.
It is an array-like object.
arguments has a length property, but it lacks all of the array methods.

The this parameter is very important in object oriented programming, and its value is determined by the invocation pattern .
There are four patterns of invocation in JavaScript:

  • the method invocation pattern,
  • the function invocation pattern,
  • the constructor invoca-tion pattern,
  • and the apply invocation pattern.

The patterns differ in how the bonus parameterthis is initialized.

There is no run-time error when the number of arguments and the number of parameters do not match. If there are too many argument values, the extra argument values will beignored.
If there are too few argument values, the undefined value will be substituted for the missing values.
There is no type checking on the argument values: any type of value can be passed to any parameter.

##The Method Invocation Pattern

When a function is stored as a property of an object, we call it a method.

// Create myObject. It has a value and an increment  

// method. The increment method takes an optional
// parameter. If the argument is not a number, then 1
// is used as the default.
var myObject = {
value: 0,
increment: function (inc) {
this.value += typeof inc === 'number' ? inc : 1;
}
};
myObject.increment(); document.writeln(myObject.value); // 1 myObject.increment(2); document.writeln(myObject.value); // 3

##The Function Invocation Pattern When a function is not the property of an object, then it is invoked as a function:

var sum = add(3, 4); // sum is 7

When a function is invoked with this pattern, this is bound to the global object.

// Augment myObject with a double method.
myObject.double = function () {
    var that = this;    // Workaround.
    var helper = function () {
        that.value = add(that.value, that.value);
    };
    helper();    // Invoke helper as a function.
};
// Invoke double as a method.
myObject.double();
document.writeln(myObject.value);

##The Constructor Invocation Pattern JavaScript is a prototypal inheritance language.
That means that objects can inherit properties directly from other objects.
The language is class-free.

// Create a constructor function called Quo.
// It makes an object with a status property.
var Quo = function (string) {
    this.status = string;
};
// Give all instances of Quo a public method
// called get_status.
Quo.prototype.get_status = function () {
    return this.status;
};
// Make an instance of Quo.
var myQuo = new Quo("confused");
document.writeln(myQuo.get_status());  //confused

Use of this style of constructor functions is not recommended.

##The Apply Invocation Pattern The apply method lets us construct an array of arguments to use to invoke a func-tion.
It also lets us choose the value of this.

// Make an array of 2 numbers and add them.  

var array = [3, 4];
var sum = add.apply(null, array); // sum is 7
// Make an object with a status member.
var statusObject = {
status: 'A-OK'
};
// statusObject does not inherit from Quo.prototype,
// but we can invoke the get_status method on
// statusObject even though statusObject does not have // a get_status method.
var status = Quo.prototype.get_status.apply(statusObject);
// status is 'A-OK'

A function always returns a value. If the returnvalue is not specified, then undefined is returned.
If the function was invoked with the new prefix and the return value is not an object, then this (the new object) is returned instead.

##Augmenting Types JavaScript allows the basic types of the language to be augmented.

Function.prototype.method = function (name, func) {
    this.prototype[name] = func;
    return this;
};

Number.method('integer', function () {
    return Math[this < 0 ? 'ceil' : 'floor'](this);
});
document.writeln((-10 / 3).integer());  // -3

String.method('trim', function () {
    return this.replace(/^\s+|\s+$/g, '');
});
document.writeln('"' + "   neat   ".trim() + '"');

##Scope JavaScript does not have block scope.
JavaScript does have function scope.

#Inheritance ##Pseudoclassical

Function.method('new', function () {
// Create a new object that inherits from the
// constructor's prototype.
    var that = Object.create(this.prototype);
// Invoke the constructor, binding –this- to
// the new object.
    var other = this.apply(that, arguments);
// If its return value isn't an object,
// substitute the new object.
    return (typeof other === 'object' && other) || that;
});

Javascript原型链的原理


说到prototype,就不得不先说下new的过程。 我们先看看这样一段代码:

var Person = function () { };
var p = new Person();

很简单的一段代码,我们来看看这个new究竟做了什么?我们可以把new的过程拆分成以下三步:
<1> var p={}; 也就是说,初始化一个对象p。
<2> p.__proto__=Person.prototype;
<3> person.call(p);也就是说构造p,也可以称之为初始化p。
关键在于第二步,我们来证明一下:

var Person = function () { };
var p = new Person();
alert(p.__proto__ === Person.prototype);

这段代码会返回true。说明我们步骤2的正确。
那么__proto__是什么?我们在这里简单地说下。每个对象都会在其内部初始化一个属性,就是__proto__, 当我们访问一个对象的属性时,如果这个对象内部不存在这个属性,那么他就会去__proto__里找这个属性, 这个__proto__又会有自己的__proto__,于是就这样一直找下去,也就是我们平时所说的原型链的概念。 按照标准,__proto__是不对外公开的,也就是说是个私有属性,但是Firefox的引擎将他暴露了出来成为了一个共有的属性,我们可以对外访问和设置。 好,概念说清了,让我们看一下下面这些代码:


var Person = function () { };
Person.prototype.Say = function () {
alert("Person say");
}
var p = new Person();
p.Say();

这段代码很简单,相信每个人都这样写过,那就让我们看下为什么p可以访问Person的Say。
首先var p=new Person();可以得出p.__proto__=Person.prototype
那么当我们调用p.Say()时,首先p中没有Say这个属性,于是,他就需要到他的__proto__中去找, 也就是Person.prototype,而我们在上面定义了Person.prototype.Say=function(){}; 于是,就找到了这个方法。 好,接下来,让我们看个更复杂的。


var Person = function () { };
Person.prototype.Say = function () {
alert("Person say");
}
Person.prototype.Salary = 50000;
var Programmer = function () { };
Programmer.prototype = new Person();
Programmer.prototype.WriteCode = function () {
alert("programmer writes code");
};
Programmer.prototype.Salary = 500;
var p = new Programmer();
p.Say();
p.WriteCode();
alert(p.Salary);

我们来做这样的推导:
var p=new Programmer() 可以得出p.proto=Programmer.prototype;
而在上面我们指定了Programmer.prototype=new Person();我们来这样拆分, var p1=new Person();Programmer.prototype=p1;那么: p1.__proto__=Person.prototype; Programmer.prototype.__proto__=Person.prototype; 由根据上面得到p.__proto__=Programmer.prototype。可以得到p.__proto__.__proto__=Person.prototype。 好,算清楚了之后我们来看上面的结果,p.Say()。由于p没有Say这个属性,于是去p.__proto__,也就是Programmer.prototype,也就是p1中去找,由于p1中也没有Say,那就去p.proto.proto,也就是Person.prototype中去找,于是就找到了alert(“Person say”)的方法。 其余的也都是同样的道理。
这也就是原型链的实现原理。
最后,其实prototype只是一个假象,他在实现原型链中只是起到了一个辅助作用,换句话说,他只是在new的时候有着一定的价值,而原型链的本质,其实在于__proto__!

JavaScript中__proto__与prototype的关系

所有构造器/函数的__proto__都指向Function.prototype,它是一个空函数(Empty function)

Number.__proto__ === Function.prototype  // true  
Boolean.__proto__ === Function.prototype // true  
String.__proto__ === Function.prototype  // true  
Object.__proto__ === Function.prototype  // true  
Function.__proto__ === Function.prototype // true   
Array.__proto__ === Function.prototype   // true  
RegExp.__proto__ === Function.prototype  // true  
Error.__proto__ === Function.prototype   // true  
Date.__proto__ === Function.prototype    // true

JavaScript中有内置(build-in)构造器/对象共计12个(ES5中新加了JSON),这里列举了可访问的8个构造器。
剩下如Global不能直接访问,Arguments仅在函数调用时由JS引擎创建,Math,JSON是以对象形式存在的,无需new。
它们的__proto__是Object.prototype。如下


Math.__proto__ === Object.prototype  // true 
JSON.__proto__ === Object.prototype  // true

上面说的“所有构造器/函数”当然包括自定义的。如下


// 函数声明
function Person() {}
// 函数表达式
var Man = function() {}
console.log(Person.__proto__ === Function.prototype) // true
console.log(Man.__proto__ === Function.prototype)    // true

这说明什么呢?

所有的构造器都来自于Function.prototype,甚至包括根构造器Object及Function自身。
所有构造器都继承了Function.prototype的属性及方法。如length、call、apply、bind(ES5)。

Function.prototype也是唯一一个typeof XXX.prototype为 “function”的prototype。其它的构造器的prototype都是一个对象。如下


console.log(typeof Function.prototype) // function
console.log(typeof Object.prototype)   // object
console.log(typeof Number.prototype)   // object
console.log(typeof Boolean.prototype)  // object
console.log(typeof String.prototype)   // object
console.log(typeof Array.prototype)    // object
console.log(typeof RegExp.prototype)   // object
console.log(typeof Error.prototype)    // object
console.log(typeof Date.prototype)     // object
console.log(typeof Object.prototype)   // object

  

噢,上面还提到它是一个空的函数,alert(Function.prototype) 下看看。

知道了所有构造器(含内置及自定义)的__proto__都是Function.prototype,那Function.prototype的__proto__是谁呢?

相信都听说过JavaScript中函数也是一等公民,那从哪能体现呢?如下

console.log(Function.prototype.__proto__ === Object.prototype) // true 这说明所有的构造器也都是一个普通JS对象,可以给构造器添加/删除属性等。
同时它也继承了Object.prototype上的所有方法:toString、valueOf、hasOwnProperty等。

最后Object.prototype的__proto__是谁?

Object.prototype.__proto__ === null // true 已经到顶了,为null。

所有对象的__proto__都指向其构造器的prototype

上面测试了所有内置构造器及自定义构造器的__proto__,下面再看看所有这些构造器的实例对象的__proto__指向谁?

先看看JavaScript引擎内置构造器


var obj = {name: 'jack'}
var arr = [1,2,3]
var reg = /hello/g
var date = new Date
var err = new Error('exception')
 
console.log(obj.__proto__ === Object.prototype) // true
console.log(arr.__proto__ === Array.prototype)  // true
console.log(reg.__proto__ === RegExp.prototype) // true
console.log(date.__proto__ === Date.prototype)  // true
console.log(err.__proto__ === Error.prototype)  // true

再看看自定义的构造器,这里定义了一个Person

function Person(name) {
    this.name = name
}
var p = new Person('jack')
console.log(p.__proto__ === Person.prototype) // true

p是Person的实例对象,p的内部原型总是指向其构造器Person的prototype。

每个对象都有一个constructor属性,可以获取它的构造器,因此以下打印结果也是恒等的

function Person(name) {
    this.name = name
}
var p = new Person('jack')
console.log(p.__proto__ === p.constructor.prototype) // true

上面的Person没有给其原型添加属性或方法,这里给其原型添加一个getName方法

function Person(name) {
    this.name = name
}
// 修改原型
Person.prototype.getName = function() {}
var p = new Person('jack')
console.log(p.__proto__ === Person.prototype) // true
console.log(p.__proto__ === p.constructor.prototype) // true

可以看到p.__proto__Person.prototypep.constructor.prototype都是恒等的,即都指向同一个对象。

如果换一种方式设置原型,结果就有些不同了

function Person(name) {
    this.name = name
}
// 重写原型
Person.prototype = {
    getName: function() {}
}
var p = new Person('jack')
console.log(p.__proto__ === Person.prototype) // true
console.log(p.__proto__ === p.constructor.prototype) // false

这里直接重写了Person.prototype(注意:上一个示例是修改原型)。
输出结果可以看出p.__proto__仍然指向的是Person.prototype,而不是p.constructor.prototype。

这也很好理解,给Person.prototype赋值的是一个对象直接量{getName: function(){}}, 使用对象直接量方式定义的对象其构造器(constructor)指向的是根构造器Object, Object.prototype是一个空对象{},{}自然与{getName: function(){}}不等。如下

var p = {}
console.log(Object.prototype) // 为一个空的对象{}
console.log(p.constructor === Object) // 对象直接量方式定义的对象其constructor为Object
console.log(p.constructor.prototype === Object.prototype) // 为true,不解释 

上面代码中用到的__proto__目前在IE6/7/8/9中都不支持。IE9中可以使用Object.getPrototypeOf(ES5)获取对象的内部原型。


var p = {}
var __proto__ = Object.getPrototypeOf(p)
console.log(__proto__ === Object.prototype) // true
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment