一个让我收益匪浅的教程。用这篇博文对一些关键知识点进行整理。
教程地址:https://johnresig.com/apps/learn/
1.goal
// The .bind method from Prototype.js
Function.prototype.bind = function(){
var fn = this, args = Array.prototype.slice.call(arguments), object = args.shift();
return function(){
return fn.apply(object,
args.concat(Array.prototype.slice.call(arguments)));
};
};
2.ways to define function
function fn(){}
var fn= function(){}
obj.fn = function(){}
order is no a matter(define promotion)
function can be defined below return//次序不重要We can refer to a function, within itself, by its name(iteration)//函数内可以调用函数自身
3.What is the name of a function?
以var fn= function doSth(){}
这种形式定义的函数,在全局中只能以fn为名来调用。
var ninja = function myNinja(){
assert( ninja == myNinja, "This function is named two things - at once!" );
};
ninja();
assert( typeof myNinja == "undefined", "But myNinja isn't defined outside of the function." );
3.1 the function which is an object property can run & what about the property removed.
匿名函数作为对属性时,不会被声明,只在被调用时执行。因而当对应属性被删除时,再调用该函数则无效。
var ninja = {
yell: function(n){
return n > 0 ? ninja.yell(n-1) + "a" : "hiy";
}
};
assert( ninja.yell(4) == "hiyaaaa", "A single object isn't too bad, either." );
var samurai = { yell: ninja.yell };
var ninja = null;
try {
samurai.yell(4);
} catch(e){
assert( false, "Uh, this isn't good! Where'd ninja.yell go?" );
}
3.2give the anonymous function a name
当属性中的函数具名时,在属性被创建并赋值时,便声明了以属性键名为名的函数。及时属性之后被清空,函数依然存在。
var ninja = {
yell: function yell(n){
return n > 0 ? yell(n-1) + "a" : "hiy";
}
};
assert( ninja.yell(4) == "hiyaaaa", "Works as we would expect it to!" );
var samurai = { yell: ninja.yell };
var ninja = {};
assert( samurai.yell(4) == "hiyaaaa", "The method correctly calls itself." );
3.3What if we don't want to give the function a name?
在匿名函数中,可以用arguments.callee调用函数自身
var ninja = {
yell: function(n){
return n > 0 ? arguments.callee(n-1) + "a" : "hiy";
}
};
assert( ninja.yell(4) == "hiyaaaa", "arguments.callee is the function itself." );
4 Functions as Objects
4.1 Is it possible to cache the return results from a function
函数作为一个对象,可以在其子对象中进行数据缓存。
function isPrime( num ) {
if ( isPrime.cache[ num ] != null )
return isPrime.cache[ num ]; //If the parm cached,return corresponding result
var prime = num != 1; // Everything but 1 can be prime
for ( var i = 2; i < num; i++ ) {
if ( num % i == 0 ) {
prime = false;
break;
}
}
isPrime.cache[ num ] = prime //cached the parm&result
return prime;
}
isPrime.cache = {};
assert( isPrime(5), "Make sure the function works, 5 is prime." );
assert( isPrime.cache[5], "Make sure the answer is cached." );
5.Context
5.1How can we change the context of a function
改变上下文的方法
var object = {};
function fn(){
return this;
}
assert( fn() == this, "The context is the global object." );
assert( fn.call(object) == object, "The context is changed to a specific object." );
.call()&.apply()也可以使用
5.2What does the new operator do?
new操作符做了什么
1.创建一个空对象作为this
2.this.proto指向构造函数的prototype
3.运行构造函数
4.返回this(如果函数没有return)
function Ninja(name){
this.changeName = function(name){
this.name = name;
};
this.changeName( name );
}
var ninja = new Ninja("John");
assert( ninja.name == "John", "The name has been set on initialization" );//function(name) has been implemented
ninja.changeName("Bob");
5.3 How to make sure that the new operator is always used
function User(first, last){
if ( !(this instanceof User) )
return new User(first, last);
this.name = first + " " + last;
}
var name = "Resig";
var user = User("John", name);
assert( user, "This was defined correctly, even if it was by mistake." );
assert( name == "Resig", "The right name was maintained." );
5.4Flexible Arguments
Array-like object can implement method of Array by change context
Math.max/Math.min/sort/slice/splice
Closures
- Closures are frequently used for callbacks/event listener/timer
- Private properties, using closures.
Instance Type
instance.construtor
Inheritance
prototypes
Extending prototypes can be dangerous.
Object.prototype.keys = function(){
var keys = [];
for ( var i in this )
keys.push( i );
return keys;
};
var obj = { a: 1, b: 2, c: 3 };
assert( obj.keys().length == 3, "We should only have 3 properties." );
delete Object.prototype.keys;
对数组进行拓展后,for in遍历将失效,多出一项拓展函数。
因为for in 会枚举原型链上的所有属性。
Enforcing Function Context
当我们在click handler上绑定一个对象方法时。因为使用对象调用函数时,this指向该对象,所以会发生如下错误。
var Button = {
click: function(){
this.clicked = true;
}
};
var elem = document.createElement("li");
elem.innerHTML = "Click me!";
elem.onclick = Button.click;
document.getElementById("results").appendChild(elem);
elem.onclick();
assert( elem.clicked, "The clicked property was accidentally set on the element" );
我们需要将context指向正确的地方
function bind(context, name){
return function(){
return context[name].apply(context, arguments);
};
}
var Button = {
click: function(){
this.clicked = true;
}
};
var elem = document.createElement("li");
elem.innerHTML = "Click me!";
elem.onclick = bind(Button, "click");
document.getElementById("results").appendChild(elem);
elem.onclick();
assert( Button.clicked, "The clicked property was correctly set on the object" );
这种绑定方法可以直接写入原型链中进行调用
Function.prototype.bind = function(object){
var fn = this;
return function(){
return fn.apply(object, arguments);
};
};//将对象方法变成一个绑定context的函数。
var Button = {
click: function(){
this.clicked = true;
}
};
var elem = document.createElement("li");
elem.innerHTML = "Click me!";
elem.onclick = Button.click.bind(Button);
document.getElementById("results").appendChild(elem);
elem.onclick();
assert( Button.clicked, "The clicked property was correctly set on the object" );
Function.prototype.bind = function(){
var fn = this, args = Array.prototype.slice.call(arguments), object = args.shift();//object作为context
return function(){
return fn.apply(object,
args.concat(Array.prototype.slice.call(arguments)));
};//将fn所需的参数提前以供使用,同时保留bind时使用的参数。
};
var Button = {
click: function(value){
this.clicked = value;
}
};
var elem = document.createElement("li");
elem.innerHTML = "Click me!";
elem.onclick = Button.click.bind(Button, false);
document.getElementById("results").appendChild(elem);
elem.onclick();
assert( Button.clicked === false, "The clicked property was correctly set on the object" );
Function Length
方程的length等于参数的个数
Function overloading
根据参数的个数,调用不同的方法。
function addMethod(object, name, fn){
// Save a reference to the old method
var old = object[ name ];
// Overwrite the method with our new one
object[ name ] = function(){
// Check the number of incoming arguments,
// compared to our overloaded function
if ( fn.length == arguments.length )
// If there was a match, run the function
return fn.apply( this, arguments );
// Otherwise, fallback to the old method
else if ( typeof old === "function" )
return old.apply( this, arguments );
};
}
这个策略中,新方法存储在封包函数中,旧方法作为封包的环境。封包函数根据参数个数来决定调用新旧、方法。类似的封包函数作为旧方法不断被调用,直至参数个数符合后,执行对应方法。