js笔试题系列之三——函数

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了js笔试题系列之三——函数相关的知识,希望对你有一定的参考价值。

  在这一章以函数为主的讲解中,也会不可避免的牵涉到数组和对象的内容,这也不难理解,知识往后走牵涉的内容自然也越多。

(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)();

题三:在setTimeoutsetInterval中的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)面向对象问题

题一:实现一个继承

 

  

以上是关于js笔试题系列之三——函数的主要内容,如果未能解决你的问题,请参考以下文章

js笔试题系列之二——数组

js笔试题系列之——基础类型与运算符

js作用域之常见笔试题,运行结果题

[笔试题]sizeof系列面试题中的易错之处

js笔试题(不定期更新)

第862期成为一名函数式码农之三