Home » Code » 前端面试&你真的懂Javascript吗

前端面试&你真的懂Javascript吗

“你真的懂Javascript”吗是国外某大牛出的5道题目(原帖在此),都是Javascript里边比较偏的细节,实际项目中几乎不会那样子写代码。但今天看到居然有的公司就直接拿来作面试题考人了。这吓得我赶紧去看看以防不测。

doyouknowjavascript
以下解析主要来自汤姆大叔。

第1题

if (!('a' in window)) {
	console.log('sb');
	var a = 1;
}
alert(a);

题目意思是说,如果a不是window的属性,声明之并赋值为1,接着弹出a的值。

第一次到这题目相信大部分人的直觉都会告诉他,答案是1,但实际答案却是undefined。要理解这道题,得先清楚以下这些知识。

首先,所有的全局变量都是winsow的属性,一个对象是否拥有某一属性,使用in关键字判断。在全局作用域内,var a = 1跟window.a = 1,其实是一回事:

function A() {}
var a = "1";
function B() {
	this.B = "B";
	var C = "C";
}
console.log(window.A === A);//true
console.log(window.a === a);//true
console.log(window.B === B);//true
console.log(window.C === C);//Uncaught ReferenceError: C is not defined

其次,所有的变量声明都在范围作用域的顶部。看下面这个例子:

alert("s" in window);//true
var s;

答案是true,是不是有点扯蛋。尽管你声明在弹出之前,但Javascript引擎时就是这么运行的,会将声明“提前”,上边两行代码互换顺序其实是一样的。再说得专业点就是执行代码分2个阶段,先进入执行上下文,这时候会将所有的变量啊函数啊之类的都准备好,这时候s已经有了,之后再执行代码。深入我就解释不清楚了,这东西你知道就会,不知道就不会,硬知识。

到这里,基本懂了吧,这道题跟下边其实是一样一样的:

var a;
if (!('a' in window)) {
	a = 1;
}
alert(a);

一开始就声明了a,if语句根本没有进入,alert出来当然就是undefined。

第2题

var a = 1,
	b = function a(x) {
		x && a(--x);
	};
alert(a);

这个就更蛋碎了,还能不能好好玩耍了,都写得什么啊,看都看不懂,更别提答题了。结果是1。个人感觉这个主要是考察变量、函数声明的问题。

大叔有文章大幅介绍了VO(Variable Object,变量对象)的相关知识,我盯着看了半天实在不知道说了啥。个人很粗劣的从最终效果上看,那就是:函数声明比变量声明更靠前!看这么个例子:

alert(a);//function a(a) {alert(a)}

var a = 10;

alert(a);//10

a = 20;

function a(a) {alert(a)};

alert(a);//20

第一个alert居然能弹出function,真是太不可思议了。就像按第1题中说的变量提前声明相当于在最开始加上var a,那结果也是undefined。有这样子的结果,那就姑且归究于“函数声明比变量声明更前一级”这样的原因吧。下面变量的声明赋值能覆盖了函数,也就都是弹出数字了。当然的,下边是不可能再调用函数a了 的,因为函数a已经不存在了,它已经变成了数字!或许也可以这么说:变量能覆盖函数,函数无法覆盖变量。

回到题目,其实就是声明了两个变量,一个a=1,一个b=function a(){}(形参及内部的东西不要管,完全独立的),b的那个a函数声明无无法覆盖变量a的,就这么理解就成了。因此你alert变量a,结果当然是1。

第3题

function a(x) {
	return x * 2;
}
var a;
alert(a);

这一题的结果不是undefined而是输出函数a。函数更提前就声明好了,后边的变量只是声明没有赋值,是不会覆盖函数声明的。注意第2题中我的用词:变量(包括赋值)会覆盖函数,没说变量声明会覆盖函数。

第4题

function b(x, y, a) {
	arguments[2] = 10;
	alert(a);
}
b(1, 2, 3);

这题会弹出10。这题主要是考察arguments这个活动对象的用法了,注意它不是数组,虽然有着一些数组的访问方法与length属性。arguments对象在进入执行上下文时被创建,它有length属性,值为函数调用时真正传递的参数个数。arguments是一个类数组,跟真实传递过来的参数按从左到右组合起来的形成的数组是一致的。这种“一致”并不是说这个类数组的值实参引用同一个内存地址,这种一致只是Javascript引擎保持的而已。因此,将arguments第3个值改为10,实参a自然也变为10。但如果a没被传递,结果依然是undefined。

function b(x, y, a) {
	console.log(arguments);
	arguments[2] = 10;
	alert(a);//undefined
}
b(1, 2);

第5题

function a() {
	alert(this);
}
a.call(null);

这个结果是[object Window]。主要是call的用法,它的第一个参数是一个对象,调用它的函数内部的this就是这个对象。如果将它赋予null或者undefined,那函数内部this就是[object Window]。硬知识,不懂也没办法了。

OK,“你真的懂Javascript吗”的5道题就是这些,怎么说呢,这里面包含的知识点,有的还是挺有必要了解一下的。有些不是很明白,也没必要去纠结,毕竟网上的答案也不是Javascript这门语言的设计者答的,谁敢说就是正确的,倒不如直接记住结果就行了。再说实际开发中真的很少会这么子写代码。

大叔的文章下还留了下面的这些题,也一并看一下。

Q1:找出数字数组中最大的元素(使用Match.max函数)

A1:

var arr = [1,4,32,43,12];
var max = Math.max.apply(null,arr);
alert(max);//43

A2:

var arr = [1,4,32,43,12];
var max = eval('Math.max(' + arr.toString() + ')');
alert(max);//43

Q2:转化一个数字数组为function数组(每个function都弹出相应的数字)

A1:

var arr = [1,4,32,43,12];
var len = arr.length;
for (var i = 0;i < len; i++) {
	arr[i] = function(a) {
		return function() {
			alert(a);
		}
	}(arr[i]);
}
arr[1]();//4

A2:

var arr = [1,4,32,43,12];
arr = arr.map(function(i) {
	return function() {
		alert(i);
	}
});
arr[1]();//4

Q3:给object数组进行排序(排序条件是每个元素对象的属性个数)

var obj = [
	{a: 1, b: 2, c: 3},
	{aa: 11},
	{aaa: 111, bbb: 222},
];
obj.sort(function(a, b) {
	return Object.keys(a).length - Object.keys(b).length;
});
console.log(obj);//按属性从少到多排

Q4:利用JavaScript打印出Fibonacci数(悲波那契数,不使用全局变量)

A1:

function Fibonicci(k) {
	if (k <= 2) {
		return 1;
	} else {
		return Fibonicci(k - 1) + Fibonicci(k -2);
	}
}
alert(Fibonicci(7));//13。其实不懂他说的不用全局变量是什么意思?打印一定范围还是打印某个?

A2:

(function(s){
	var out = [1,1];
	if (s <= 2) {
		return 1;
	} else {
		while (s >= 3) {
			out[out.length] = out[out.length - 1] + out[out.length - 2];
			s--;
		}
	}
	console.log(out);// [1, 1, 2, 3, 5, 8, 13]
})(7)

Q5:实现如下语法的功能:var a = (5).plus(3).minus(6); //2

Number.prototype.plus = function(a) {
	return this + a;
};
Number.prototype.minus = function(a) {
	return this - a;
}
alert((5).plus(3).minus(2));//6

Q6:实现如下语法的功能:var a = add(2)(3)(4); //9

function add(num){
	var total = num;
	var fn = function(d) {
		total += d;
		return fn;
	}
	fn.toString = fn.valueOf = function() {return total};
	return fn;
}
var s = add(2)(3)(4);
alert(s);//9

这最后一题,主要就是考察的是重载valueOf(计算运行和console.log都用的它的值)、重载valueOf(alert用的这它的值)。另一种方法是将结果存到函数的一个属性上。但这种方法只是能模拟出一次效果,第二次会将第一次的结果也算上,其实是不可取的。还是得第一种。

function add(num){
	add.valueOf = add.toString = function() {return add.sum};
	add.sum = add.sum ? add.sum + num : num;
	return add;
}
var s = add(2)(3)(4);
alert(s);//9
alert(add(10));//19,会将第一次的9也带上。其实不可取

当然,正美大神那边也有类似题目的摘录,但感觉实在太变态了,都要成Javascript解释器了。套用现在流行的一句话:然而并没有什么卵用!

参考链接:
http://www.cnblogs.com/TomXu/archive/2012/02/10/2342098.html
http://www.cnblogs.com/xuld/articles/2344909.html
http://www.cnblogs.com/rubylouvre/archive/2010/02/13/1667565.html

Leave a Reply

Your email address will not be published. Required fields are marked *

*

Time limit is exhausted. Please reload CAPTCHA.