var canFly = function(){ return true; };
window.isDeadly = function(){ return true; };
assert( isNimble() && canFly() && isDeadly(), "Still works, even though isNimble is moved." );
function isNimble(){ return true; }
上述例子说明函数可以定义在任何地方,javascript在执行之前会将所有的变量和函数进行升级
assert( typeof canFly == "undefined", "canFly doesn't get that benefit." );
assert( typeof isDeadly == "undefined", "Nor does isDeadly." );
var canFly = function(){ return true; };
window.isDeadly = function(){ return true; };
上述代码又说明,如果是赋值方式定义函数指针的话,必须得在调用该函数指针之前定义,否则将无法访问
原因:
在javascript中函数的定义和变量的定义都为提升到global域当中,但变量只有定义会提升,其值并不提升,因此你会看到undefined,下述同理,stealthCheck函数在执行之前会对所有内部的函数和变量进行升级
function stealthCheck(){
assert( stealth(), "We'll never get below the return, but that's OK!" );
return stealth();
function stealth(){ return true; }
}
stealthCheck();
赋值的命名函数其函数名只在函数内部可以识别出
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." );
log( ninja );
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?" );
}
V.S. 不同之处在与上述的递归是用了ninja.yell而下面是给了该匿名函数一个函数名,因此递归时不依赖于类名,因此类ninja是不是空不会对其造成影响
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." );
如果你要坚持使用匿名函数的话,就必须采用以下方式
var ninja = {
yell: function(n){
return n > 0 ? arguments.callee(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 {
assert( samurai.yell(4) == "hiyaaaa", "A single object isn't too bad, either." );
} catch(e){
assert( false, "Uh, this isn't good! Where'd ninja.yell go?" );
}
如何在javascript中对数据进行cache
function getElements( name ) {
var results;
if ( getElements.cache[name] ) {
results = getElements.cache[name];
} else {
results = document.getElementsByTagName(name);
getElements.cache[name] = results;
}
return results;
}
getElements.cache = {};
log( "Elements found: ", getElements("pre").length );
log( "Cache found: ", getElements.cache.pre.length );
function isPrime( num ) {
if ( isPrime.cache[ num ] != null )
return isPrime.cache[ num ];
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
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." );
更改this的context的几种方式:
1、将this放入一个object当中
function katana(){
this.isSharp = true;//这里的this代表global context
}
katana();
assert( isSharp === true, "A global object now exists with that name and value." );
var shuriken = {
toss: function(){
this.isSharp = true;//这里的this代表shuriken这个context
}
};
shuriken.toss();
assert( shuriken.isSharp === true, "When it's an object property, the value is set within the object." );
2、利用call来改变被调用函数中的this的context
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." );
apply和call的区别
function add(a, b){
return a + b;
}
assert( add.call(this, 1, 2) == 3, ".call() takes individual arguments" );
assert( add.apply(this, [1, 2]) == 3, ".apply() takes an array of arguments" );
因此下面两种写法均成立
function loop(array, fn){
for ( var i = 0; i < array.length; i++ ) {
fn.call(array,i);
}
}
var num = 0;
loop([0, 1, 2], function(value){
assert(value == num++, "Make sure the contents are as we expect it.");
assert(this instanceof Array, "The context should be the full array.");
});
function loop(array, fn){
for ( var i = 0; i < array.length; i++ ) {
fn.apply(array,[i]);
}
}
var num = 0;
loop([0, 1, 2], function(value){
assert(value == num++, "Make sure the contents are as we expect it.");
assert(this instanceof Array, "The context should be the full array.");
});
实例的属性仅存在于实例当中
function Ninja(){
this.name = "Ninja";
}
var ninjaA = Ninja();
assert( !ninjaA, "Is undefined, not an instance of Ninja." );
var ninjaB = new Ninja();
assert( ninjaB.name == "Ninja", "Property exists on the ninja instance." );
下面的this都是属于Ninja实例的范围
function Ninja(){
this.swung = false;
// Should return true
this.swingSword = function(){
this.swung = !this.swung;
return this.swung;
};
}
var ninja = new Ninja();
assert( ninja.swingSword(), "Calling the instance method." );
assert( ninja.swung, "The ninja has swung the sword." );
var ninjaB = new Ninja();
assert( !ninjaB.swung, "Make sure that the ninja has not swung his sword." );
从下述例子中我们可以清楚地看到,当没有new一个object的时候,this属于global context,如果new了,this属于那个instance的context
function User(first, last){
this.name = first + " " + last;
}
var user = User("John", "Resig");
assert( typeof user == "undefined", "Since new wasn't used, the instance is undefined." );
assert( name == "John Resig", "now this is in the global context" );
var user = new User("John", "Resig");
name = "Yin Zhang";
assert( typeof user != "undefined", "Since new wasn't used, the instance is undefined." );
assert( name == "Yin Zhang", "now this is in the global context" );
assert( user.name == "John Resig", "now this is in the global context" );
所以为了避免忘记new,我们可以做以下这番处理
function User(first, last){
if ( !(this instanceof arguments.callee) )
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." );
如下是一个很精妙的javascript处理,首先merge函数当中只有一个参数root,默认只接收第一个参数,如下所示即为{name: "John"},然而现实中它又传了一个参数{city: "Boston"},因此它将这个键值对又插入到root这个字典当中然后返回,因为root本身已经包含了{name: "John"},因此循环的时候下标是从1开始而不是0,因为0就是本身
function merge(root){
for ( var i = 1; i < arguments.length; i++ )
for ( var key in arguments[i] )
root[key] = arguments[i][key];
return root;
}
var merged = merge({name: "John"}, {city: "Boston"});
assert( merged.name == "John", "The original name is intact." );
assert( merged.city == "Boston", "And the city has been copied over." );
Javascript求最大和最小数值的方法
function smallest(){
return Math.min.apply( Math, arguments );
}
function largest(){
return Math.max.apply( Math, arguments );
}
assert(smallest(0, 1, 2, 3) == 0, "Locate the smallest value.");
assert(largest(0, 1, 2, 3) == 3, "Locate the largest value.");
Javascript将数组中的数字从大到小进行排序,注意只有真正的Array才有sort方法,因此在这里我们要将传入的arguments转换成array进而调用sort方法
function highest(){
return makeArray(arguments).sort(function(a,b){
return b - a;
});
}
function makeArray(array){
return Array().slice.call( array );
}
assert(highest(1, 1, 2, 3)[0] == 3, "Get the highest value.");
assert(highest(3, 1, 2, 3, 4, 5)[1] == 4, "Verify the results.");
以下我们将array中的除第一个元素以外的元素都抽取出来然后取出剩余元素当中的最大值,乘以第一个元素,这里multi其实就代表了arguments中的第一个元素
function multiMax(multi){
// Make an array of all but the first argument
var allButFirst = Array().slice.call( arguments, 1 );
// Find the largest number in that array of arguments
var largestAllButFirst = Math.max.apply( Math, allButFirst );
// Return the multiplied result
return multi * largestAllButFirst;
}
assert( multiMax(3, 1, 2, 3) == 9, "3*3=9 (First arg, by largest.)" );
以下是一个典型的callback应用,这里我们利用得到的test.html的markup替换results里头的markup
var results = jQuery("#results").html("<li>Loading...</li>");
jQuery.get("test.html", function(html){
results.html( html );
assert( results, "The element to append to, via a closure." );
});
下面这个例子说明,timer和count都是在global closure之中,因此哪里都能访问到
var count = 0;
var timer = setInterval(function(){
if ( count < 5 ) {
log( "Timer call: ", count );
count++;
} else {
assert( count == 5, "Count came via a closure, accessed each step." );
assert( timer, "The timer reference is also via a closure." );
clearInterval( timer );
}
}, 100);
下面这个例子告诉我们如何定义一个私有变量
function Ninja(){
var slices = 0;
this.getSlices = function(){
return slices;
};
this.slice = function(){
slices++;
};
}
var ninja = new Ninja();
ninja.slice();
assert( ninja.getSlices() == 1, "We're able to access the internal slice data." );
assert( ninja.slices === undefined, "And the private data is inaccessible to us." );
这里c==undefined就像前面说的,declaration可以hoist,但是其数值不能提升,因此innerRun的时候可以感觉到它的定义,但无法感知其数值
var a = 5;
function runMe(a){
assert( a == 6, "Check the value of a." );
function innerRun(){
assert( b == 7, "Check the value of b." );
assert( c == undefined, "Check the value of c." );
}
var b = 7;
innerRun();
var c = 8;
}
runMe(6);
for ( var d = 0; d < 3; d++ ) {
setTimeout(function(){
assert( d == 3, "Check the value of d." );
}, 100);
}
下面更进一步说明了closure的概念,利用匿名函数人为制造一个closure与global隔开
(function(){
var count = 0;
var timer = setInterval(function(){
if ( count < 5 ) {
log( "Timer call: ", count );
count++;
} else {
assert( count == 5, "Count came via a closure, accessed each step." );
assert( timer, "The timer reference is also via a closure." );
clearInterval( timer );
}
}, 100);
})();
assert( typeof count == "undefined", "count doesn't exist outside the wrapper" );
assert( typeof timer == "undefined", "neither does timer" );
Two ways of creating wrapper function
(function(){
var myLib = window.myLib = function(){
// Initialize
};
// ...
})();
var myLib = (function(){
function myLib(){
// Initialize
}
// ...
return myLib;
})();
下面是个很重要的例子,请自行想清楚原因
var count = 0;
for ( var i = 0; i < 4; i++ ) {
setTimeout(function(){
assert( i == 4, "Check the value of i." );
}, i * 200);
}
var count = 0;
for ( var i = 0; i < 4; i++ ) (function(i){
setTimeout(function(){
assert( i == count++, "Check the value of i." );
}, i * 200);
})(i);