Skip to content

Latest commit

 

History

History
316 lines (234 loc) · 7.64 KB

func.md

File metadata and controls

316 lines (234 loc) · 7.64 KB

函数

函数是由事件驱动的或者当它被调用时执行的可重复使用的代码块。

快速导航

面试指南

函数声明与函数表达式

对于函数声明解释器会首先读取,并使其在执行任何代码之前可用;对于函数表达式,则必须等到解释器执行到它所在的代码行,才会被真正解析。

例如下面例子,函数表达式test2必须声明在其调用之前才可用

console.log(test1(1, 2)); // 3
console.log(test2(1, 2)); // test2 is not defined

//函数声明
function test1(a, b){
    return a + b;
}

//函数表达式
const test2 = function f(a, b){
    return a + b;
}

函数表达式实现一个阶乘函数

const factorial = (function f(num){
    if(num <= 1){
        return 1;
    }else{
        return num * f(num -1);
    }
});

console.log(factorial(3)); // 6

内置函数

  • push()

数组添加新值后的返回值,返回当前数组的Length。

let a = [].push('test'); 输出a的值为1而不是['test'],因为push()返回的是数组的长度。

let a  = [];
a.push('test');
console.log(a); //['test']

arguments对象

系统内置的arguments对象,可以用于获取函数参数、参数长度等

面试:递归调用实现一个阶乘函数

下面程序最终输出结果为6,实现了一个3 * 2 * 1的阶乘函数,不明白之处,参考 知乎讨论

function sum(num){
    if(num <= 1){

        //获取参数长度
        console.log('arguments.length: ', arguments.length);

        return 1;
    }else{
        return num * arguments.callee(num-1);
    }
}

console.log(sum(3));

call和apply的使用与区别

  • apply使用情况
function box(num1,num2){
    return num1+num2;
}

function sum(num1,num2){
    //this 表示全局作用域,浏览器环境下window,node环境global,[]表示传递的参数
    return box.apply(this,[num1,num2]);

    //或者下面写法arguments可以当数组传递
    //return box.apply(this,arguments);
}

console.log(sum(10,10)); //输出结果: 20
  • call的使用示例
function box(num1,num2){
    return num1+num2;
}

function sum2(num1,num2){
    return box.call(this,num1,num2);
}

console.log(sum(10,10)); //输出结果: 20

总结两种情况区别: apply传递参数是按照数组传递,call是一个一个传递

引用传递

javascript没有引用传递,如果传递的参数是一个值,是按值传递;如果传递的是一个对象,则传递的是一个对象的引用。

  • 示例一:js代码按值传递

如果按引用传递,那么函数里面的num会变成类似全局变量,最后输出60

function box(num){ // 按值传递
    num+=10;
    return num;
}
var num=50;

console.log(box(num));  // 60
console.log(num);	    // 50
  • 示例二:php代码传递一个参数:

php中的引用传递,会改变外部的num值,最后num也会输出60。

function box(&$num){ 
    //加上&符号将num变成全局变量
    $num+=10;
    return $num;
}
$num = 50;
echo box($num);	// 60
echo $num;	// 60
  • 示例三:js代码传递一个对象
function box(obj){ // 按对象传递
    obj.num+=10;

    return obj.num;
}
var obj = { num: 50 };

console.log(box(obj));  // 60
console.log(obj.num);	// 60

匿名函数与闭包

匿名函数就是没有名字的函数,闭包是可访问一个函数作用域里变量的函数,由于闭包作用域返回的局部变量资源不会被立刻销毁回收,所以可能会占用更多的内存。过度使用闭包会导致性能下降,建议在非常有必要的时候才使用闭包。

匿名函数的自我执行

(function(num){
        return num;
    })(1) //1

函数里放一个匿名函数将会产生闭包

  1. 使用局部变量实现累加功能。
  2. 定义函数test1,返回一个匿名函数形成一个闭包
  3. test1赋给test2,此时test2会初始化变量a,值为test1返回的匿名函数
  4. 执行test2()
{
function test1(){
    var a = 1;

    return function(){
        // a++;
        // return a;
        // 或以下写法
        return ++a;
    }
}

var test2 = test1();

console.log(test2()); // 2
console.log(test2()); // 3
console.log(test2()); // 4

//不能这样写,这样外层函数每次也会执行,从而age每次都会初始化
console.log(test1()()); // 2
console.log(test1()()); // 2
console.log(test1()()); // 2
}

闭包中使用this对象将会导致的一些问题

在闭包中使用this对象也可能会导致一些问题,this对象是在运行时基于函数的执行环境绑定的,如果this在全局范围就是window,如果在对象内部就指向这个对象。而闭包却在运行时指向window的,因为闭包并不属于这个对象的属性或方法

返回object

var box={
    getThis:function(){
        return this;
    }
}

console.log(box.getThis()); // { getThis: [Function: getThis] }

闭包中的this将返回全局对象,浏览器中window对象,Node.jsglobal对象,可以使用对象冒充或者赋值来解决闭包中this全局对象问题。

var box={
    user: 'zs',
    getThis:function(){
        return function(){
            return this;   
        };
    }
}

console.log(box.getThis()());

对象冒充

var box={
    user: 'zs',
    getThis:function(){
        return function(){
            return this;   
        };
    }
}

console.log(box.getThis().call(box)); // { user: 'zs', getThis: [Function: getThis] }

赋值

var box={
    user: 'zs',
    getThis:function(){
        var that = this; // 此时的this指的是box对象
        return function(){
            return that.user;   
        };
    }
}

console.log(box.getThis()()); 

一个例子看懂循环和闭包之间的关系

下例,循环中的每个迭代器在运行时都会给自己捕获一个i的副本,但是根据作用域的工作原理,尽管循环中的五个函数分别是在各个迭代器中分别定义的,但是它们都会被封闭在一个共享的全局作用域中,实际上只有一个i,结果每次都会输出6

for(var i=1; i <= 5; i++){
	setTimeout(function(){
		console.log(i);
	})
}

解决上面的问题,在每个循环迭代中都需要一个闭包作用域,下面示例,循环中的每个迭代器都会生成一个新的作用域。

for(var i=1; i <= 5; i++){
	(function(j){
		setTimeout(function(){
			console.log(j);
		})
	})(i)
}

也可以使用let解决,let声明,可以用来劫持块作用域,并且在这个块作用域中声明一个变量。

for(let i=1; i <= 5; i++){
	setTimeout(function(){
		console.log(i);
	})
}