Advanced Javascript题目
#2: Goal: To be able to understand this function:
1
2
3
4
5
6
7
8
// 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)));
};
};
要理解这段代码,首先要了解javascript的数组如下基本操作。
- slice() 操作截取数组的一段, slice(start,end)则表示截取数组的start到end的一段,默认时为数组从头到尾。
- shift()返回数组的第一个元素并删除。
- concat()数组拼接
涉及到一点函数知识,比如call与apply的区别。其实,call与apply的作用没有区别,都是给函数绑定对象,只不过call传递的是参数,apply传递的是数组。
1
2
func.call(this, arg1, arg2);
func.apply(this, [arg1, arg2])
到此时,这段代码就可以基本读懂了。arguments不是数组,将其变成数组用了一步巧妙的args = Array.prototype.slice.call(arguments)
因为arguments不是array,所以原本没有slice方法,不过Array.prototype有。随后用call将其绑定于arguments。
到后面有一步concat,是把bind()可能带有的参数加进去,这段代码也就解释清楚了。
#11 the name of a function–匿名函数
1
2
3
4
5
6
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 );
这段代码的输出结果为
PASS This function is named two things - at once!
PASS But myNinja isn't defined outside of the function.
LOG function myNinja(){ assert( ninja == myNinja, "This function is named two things - at once!" ); }
说明匿名函数在函数外可以看作undefined,而实名函数则不会。
#12: We can even do it if we’re an anonymous function that’s an object property.
1
2
3
4
5
6
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." );
根据输出我们发现ninja.yell虽然定义在对象ninja内,仍然可以在对象外部调用它。
#13: But what happens when we remove the original object?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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?" );
}
通过输出结果
PASS A single object isn't too bad, either.
FAIL Uh, this isn't good! Where'd ninja.yell go?
发现原来对象的赋值实际上是指针,指向了原来的对象,所以在删除了原来对象的时候,没有办法调用到这个函数。
#14: Let’s give the anonymous function a name!
1
2
3
4
5
6
7
8
9
10
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." );
通过比较之前的代码,我们发现如果不给匿名函数命名,我们即便是在函数里调用这个函数也要用对象的方法ninja.yell(n-1)
,命名之后我们就可以直接通过yell(n-1)
来直接调用了。如果我们不想给它命名,也可以通过arguments.callee(n-1)
来调用。
#19: Is it possible to cache the return results from a function?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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 );
可以通过cache的方式将函数运行的结果存储起来。
#20: QUIZ: Can you cache the results of this function?
1
2
3
4
5
6
7
8
9
10
11
12
13
function isPrime( 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;
}
}
return prime;
}
assert( isPrime(5), "Make sure the function works, 5 is prime." );
assert( isPrime.cache[5], "Is the answer cached?" );
首先,看一道前端面试题目
1
2
3
4
5
var a = {n:1};
var b = a; // 持有a,以回查
a.x = a = {n:2};
alert(a.x);// --> undefined
alert(b.x);// --> {n:2}
它的结果为什么是这样。
对于这个问题,segmentfault上面一位用户给出了详尽的解答
同意3楼和4楼同学说的。连等是先确定所有变量的指针,再让指针指向那个赋值(
{n:3}
)。对于
a.x = a = {n:2}
,楼主原先的思路应该是:
- 先把
{n:2}
赋值给 a- 然后再创建 a.x,将
{n:2}
再赋值给 a.x这样似乎确实说不通 a.x 的值是 undefined,因为 a.x 确实是被赋值了的啊。 可是事实上,a.x 的值却是 undefined。
再来看一下这个:
a = a.x = {n:2}
的话,按楼主原先的思路应该是:
- 先把
{n:2}
赋值给 a.x,那么也就相当于b.x = {n:2}
啦- 再把 a 重新指向
{n:2}
。那么这是后 a.x 的值确实是 undefined,a 对象{n:2}
中就没有 x 属性嘛。按楼主的思路,上述两种方式的结果应该是不同的。但事实却是
a = a.x = {n:2}
和a.x = a = {n:2}
的结果是一致的。所以楼主的那种赋值的思路是不对的。事实上,解析器在接受到
a = a.x = {n:2}
这样的语句后,会这样做:
- 找到 a 和 a.x 的指针。如果已有指针,那么不改变它。如果没有指针,即那个变量还没被申明,那么就创建它,指向 null。 a 是有指针的,指向
{n:1}
;a.x 是没有指针的,所以创建它,指向 null。- 然后把上面找到的指针,都指向最右侧赋的那个值,即
{n:2}
。所以执行以后,就有了如下的变量关系图。楼主可以慢慢体会下,想通了就很简单的。
其实这道题目并不需要这些知识就可以解决,我们只是借用它去理解prime = num != 1来判断当num == 1时的prime值。
正确答案如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function isPrime( num ) {
if(isPrime.cache[num]) return isPrime.cache[num];
else {
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], "Is the answer cached?" );
#27: QUIZ: How can we implement looping with a callback?
1
2
3
4
5
6
7
8
9
10
function loop(array, fn){
for ( var i = 0; i < array.length; i++ ) {
// Implement me!
}
}
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.");
});
一开到这个问题还有点头晕,不过想到是context里面的题目,于是想到solution
1
2
3
4
5
6
7
8
9
function loop(array, fn){
for ( var i = 0; i < array.length; i++ )
fn.call( array, array[i], i );
}
var num = 0;
loop([0, 1, 2], function(value, i){
assert(value == num++, "Make sure the contents are as we expect it.");
assert(this instanceof Array, "The context should be the full array.");
});
本页为原创内容,转载请注明出处http://localhost:4000/2017/03/16/Learning-Advanced-Javascript.html