在这一章以函数为主的讲解中,也会不可避免的牵涉到数组和对象的内容,这也不难理解,知识往后走牵涉的内容自然也越多。
(1)经典作用域问题
题一:
var a = 0 function f(){ console.log(a); ==>undefined var a = 2; console.log(a); ==>2 } f()
第二次日志输出结果相对容易理解,关键是第一次打印结果并不是全局变量中的a,因为按照作用域链的查找规则,在函数执行的时候会先查找局部作用域,而此时局部作用域已经存在变量a,只不过还没有执行到赋值阶段,所以打印结果为undefined。
题二:
var a = 0 function f(a){ console.log(a); ==>1 var a = 2; console.log(a); ==>2 } f(1)
相比题一,这里多了个同名参数a,在函数体执行之前,a就已经被实参赋值,所以打印结果为1。
关于作用域链:函数在执行的时候,会在内部生成执行上下文对象,它包含了两个属性,一个是活动对象,一个是scope属性;活动对象在函数体执行之初就被实参、函数体内变量、this对象、argument对象填充,scope属性指向上层函数的活动对象,当执行函数体内的变量查找时,会首先在本函数活动对象内查找,如果未找到,则根据scope指向的上层函数的活动对象内查找,以此类推,一直往上查找到全局window对象。
(2)预编译期与执行期
var f = function(){console.log(1)} function f(){console.log(2)} f() ==>1
这里不能简单的以为js代码从上至下执行,所以下一个函数定义会覆盖上一个函数定义。实际上js在执行之前存在一个预编译期,所有通过var声明的变量和function声明的函数都在js的预编译期进行处理(包括对变量名进行索引和对function函数体进行解析),function函数体内部语句和相关的逻辑解析后,会存储在函数变量所指向的地址中等待函数调用。而在js的执行期,f又被重新赋值改变了引用地址,而实际上执行期间function f(){console.log(2)}这条声明式的语句并不会再去执行(在预编译期间已经处理)或产生任何其它改变,所以最终结果为1。
思考题:
function test() { alert("1"); } test(); function test() { alert("2"); } test();
(3)函数判断数据类型并返回
function getType(obj){ return Object.prototype.toString.call(obj).slice(8,-1); }
这种方法相比于使用typeof/instance/constructor等更加便捷,也是目前使用最多的方法。它利用了Object内置对象原型方法返回各类型特殊字符串的性质,可以全部区分出8种数据类型。但实际上Object.prototype.toString方法并不是专门为区分数据类型而设计的,它返回的字符串类型也并不是仅仅有8种,如果有必要需要考虑这两种特殊情况:arguments对象返回'Arguments',dom对象返回'HTMLDivElement'。
(4)小试牛刀:函数实现取最大值,函数参数个数不限,比如fun(1,2)返回2,fun(1,2,3,4)返回4
方法一:
function max(){ var arr = [].slice.call(arguments,0); arr.sort(function(a,b){ return a-b; }) return arr.pop(); }
这里考察了arguments对象的使用和排序问题,需要注意的是这里需要将arguments对象先转换为真正的数组,才能使用sort方法,这里使用了[].slice.call,等效于Array.prototype.slice.call,它们地址引用是相同的。当然如果你换成下面这种解题答法我相信面试官会更开心的。
方法二:
function max(){ return Math.max.apply(null,arguments); }
当然,这里并没有对参数类型进行考虑,如果要考虑一些异常情况或者,可以把这些特殊处理加上去。
(5)函数、数组、对象综合题
已有数据对象为:
var students = [{ name:'lilei', sex:'girl', scores:{ Math:88, English:69 } }, ...... ]
函数功能需求(1):
//功能需求:日志打印出存放男学生或女学生或全部学生的名字的数组 function getStudent(sex){ //ToDo
...... console.log(arr); } getStudent();
解答:
function getStudent(sex){ var arr = []; //存放最终结果 for(i in students){ if(sex == undefined){ arr.push(students[i].name); }else if(students[i].sex == sex){ arr.push(students[i].name); } } console.log(arr); }
函数功能需求(2):
//功能需求:日志打印出存放某科目所有学生成绩的数组 function getScore(Subject){ //ToDo ... console.log(arr); } //打印所有学生数学成绩 //getScore('Math');
解答:
function getScore(Subject){ var arr = []; //存放该科目所有成绩 for(i in students){ arr.push(students[i].scores[Subject]); } console.log(arr); }
函数功能需求(3):
//功能需求:日志打印一个对象,该对象为某科目为最低分或最高分的学生名字、对于科目成绩 function getObj(Subject,maxOrMin){ //ToDo ... console.log(obj); } //打印数学最高分的学生名字和数学成绩:{name:***,Math:**} getObj('English','min');
解答:
function getObj(Subject,maxOrMin){ var obj = {}; var student; students.sort(function(a,b){ return a.scores[Subject] - b.scores[Subject]; //按成绩从小到大排列 }) if(maxOrMin == 'max'){ student = students[students.length-1]; }else{ student = students[0]; } obj.name = student.name; obj[Subject] = student.scores[Subject]; }
(6)函数中的this应用场景
题一:普通函数中的this
var a = 0; function fun(){ var a = 'fun'; console.log(this.a); ==>0 }
普通函数中的this指向window对象
题二:方法中的this
var a = 0; var obj = { a:'obj', fun:function(a){ console.log(this.a); ==>'obj' } } obj.fun(1);
当函数作为对象的方法(对象的属性值为一个函数时,我们称此函数为该对象的方法)调用时,方法中的this指向调用方法的对象。
思考题:
var a = 0; var obj = { a:'obj', fun:function(a){ return function(){ console.log(this.a); ==> ? 自己动手试试,然后思考为什么 } } } obj.fun(1)();
题三:在setTimeout和setInterval中的this
var a = 0; var obj = { a:'obj', fun:function(a){ setTimeout(function(){ console.log(this.a); ==>0 },1000) } } obj.fun(1);
在setTimeout和setInterval中this永远指向window,所以,假设在题三中期望输出结果为obj对象的a属性值,则可以这样修改:
var a = 0; var obj = { a:'obj', fun:function(){ var _this = this; setTimeout(function(){ console.log(_this.a); ==>'obj' },1000) } } obj.fun();
题四:作为构造函数调用
var a = 0; function Obj() { this.a = 1; } var o = new Obj(); console.log(o.a); ==>1
这是个简单的构造函数,在new操作过程中,实例化对象将在构造函数中替代this指向,相当于Obj.call(o)。
题五:在dom元素的事件句柄中
<div onclick="fun(this)">ddd</div> <script> function fun(el) { console.log(el); ==>触发事件句柄的dom对象 } </script>
(7)面向对象问题
题一:实现一个继承