• 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)面向对象问题

    题一:实现一个继承

      

  • 相关阅读:
    SQL语句-用sql语句得到表中所有字段的名字
    SQL语句-判断数据库中是否有这张表
    Java开发-创建第一个项目hello world
    MFC-一个很简单的程序最小化到托盘,带右键菜单带图标
    Everything SDK-基于名称快速定位文件和文件夹。
    NX二次开发-基于PycharmIDE的NXOpen Python开发环境配置
    手动局域网搭建FTP服务器
    FreeCAD二次开发-基于控制台模式FC外部开发
    FreeCAD二次开发-基于PyQT对话框与FC交互的开发
    实例开发-NX二次开发手动截图工具
  • 原文地址:https://www.cnblogs.com/webLilingyun/p/5515842.html
Copyright © 2020-2023  润新知