资讯专栏INFORMATION COLUMN

JavaScript基础---函数

JackJiang / 3259人阅读

摘要:一函数没有函数重载函数参数对象,类数组对象正是由于函数体内使用对象接收传递的参数,所以即便你定义的函数只接收两个参数,在调用函数时也未必一定要传递两个参数。

一、JavaScript函数没有函数重载 1.函数参数arguments对象,类数组对象

正是由于函数体内使用arguments对象接收传递的参数,所以即便你定义的函数只接收两个参数,在调用函数时也未必一定要传递两个参数。

2.函数重载

在其他语言中如Java,所谓函数重载就是方法名相同参数不同的所有方法,因为在Java中只要函数签名(接受的参数类型和数量)不同,就认为是不同的函数。但是在JavaScript中函数没有签名,所以做不到真正的函数重载

3.使用arguments对象来弥补没有函数重载的缺憾
function doAdd(){
    if(arguments.length == 1){
        return arguments[0]+10;
    }else if(arguments.length == 2){
        return arguments[0]+arguments[1];
    }else{
        let sum = 0;
        for(let i = 0; i < arguments.length; i++){
            sum += arguments[i];
        }
        return sum;
    }
}
二、函数声明与函数表达式 1.函数声明语法
function 函数名(参数:可选) {
    函数体
}

如果函数在函数体内,或者位于程序的最顶部的话,那它就是一个函数声明

2.函数表达式
function 函数名(可选)(参数:可选) {
    函数体
}

如果函数作为表达式的一部分那么该函数就是函数表达式;
创建一个function关键字后没有标识符的函数,这种情况创建的函数叫匿名函数

3.函数声明提升

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

sayHello();  //hello
function sayHello(){
    console.log("hello");
}

上述代码不会报错,因为在代码开始执行之前,解析器就已经通过一个名为函数声明提升的过程读取并将函数声明添加到执行环境中

sayHello();  //报错
var sayHello = function(){
    console.log("hello");
}

上述代码会产生错误,因为函数位于一个赋值语句中,在执行到函数所在语句之前,变量sayHello中不会保存对函数的引用

三、递归 1.什么是递归函数

递归函数是在一个函数中调用自身所构成的

function recursion(num){
    if(num <= 1){
        return 1;
    }
    else{
        return num * recursion(num-1);
    }
}

上述函数表面上看没什么问题,但是如果做如下操作就会出问题

var temp = recursion;
recursion = null;
temp(10); //报错:recursion不是个函数

第一句代码temp指向了原始函数,recursion不再指向原始函数,调用temp(10)的时候用到了recursion(num-1),但是recursion已经不再指向原始函数,所以运行报错

2.arguments.callee

上述情况使用arguments.callee来解决问题,arguments.callee是一个指向正在执行的函数的指针,使用arguments.callee来代替函数名更保险

function recursion(num){
    if(num <= 1){
        return 1;
    }
    else{
        return num * arguments.callee(num-1);
    }
}
3.命名函数表达式
var recursion = (function recur(num){
    if(num <= 1){
        return 1;
    }
    else{
        return num * recur(num-1);
    }
})

上述代码中即便把函数赋值给另一个变量,函数的名字recur仍然有效,所以不会影响递归

四、闭包 1.闭包的作用域链

闭包是指有权访问另一个函数作用域中的变量的函数

function createComparisonFunction(propertyName){
    return function(object1,object2){
        var value1 = object1[propertyName];
        var value2 = object2[propertyName];
        if(value1 < value2){
            return -1;
        } else if(value1 > value2){
            return 1;
        } else{
            return 0;
        }
    }
}
var compare = createComparisonFunction("name");
var result = compare({name:"Nicholas"},{name:"Greg"});
compare = null;

在匿名函数从createComparisonFunction()中被返回后,它的作用域链被初始化为包含createComparisonFunction()函数的活动对象和全局变量对象。createComparisonFunction()函数执行完毕后,其执行环境的作用域链会被销毁但是它的活动对象不会被销毁,因为匿名函数的作用域链仍然在引用这个活动对象;直到匿名函数被销毁后,createComparisonFunction()的活动对象才会被销毁

2.闭包与变量

闭包只能取得包含函数中任何变量的最后一个值,闭包所保存的是整个变量对象,而不是某个特殊的变量

function createFunctions(){
    var result = new Array();
    for(var i = 0; i < 10; i++){
        result[i] = function(){
            return i;
        };
    }
    return result;
}
var result = createFunctions();
result.forEach(function(func){
    console.log(func()); //10
})

上述代码每个函数都会返回10,因为每个函数的作用域链中都保存着createFunctions()函数的活动对象,所以它们引用的都是同一个变量i。当createFunctions()函数返回后,变量i的值是10,此时每个函数都引用着保存变量i的同一个变量对象,所以每一个函数内部的i的值都是10。如果上述代码中的var i改成let i就会出现预期的效果,因为let声明的变量i属于for循环块级作用域中不属于createFunctions()函数的活动对象

function createFunctions(){
    var result = new Array();
    for(var i = 0; i < 10; i++){
        result[i] = function(num){
            return function(){
                return num;
            }
        }(i);
    }
    return result;
}
var result = createFunctions();
result.forEach(function(func){
    console.log(func()); //10
})

把i作为参数传给num,使得result数组中的每个函数都有自己的num变量副本

五、关于this 1.this对象

this对象是在运行时基于函数的执行环境绑定的,在全局函数中this等于window,而当函数作为某个对象的方法调用时,this等于那个对象。不过,匿名函数的执行环境具有全局性,因此其this对象通常指向window

var name = "The Window";
var object = {
    name: "The Object",
    getNameFunc: function(){
        return function(){
            return this.name;
        }
    }
}
console.log(object.getNameFunc()()); //The Window
var name = "The Window";
var object = {
    name: "The Object",
    getNameFunc: function(){
        var that = this;
        return function(){
            return that.name;
        }
    }
}
console.log(object.getNameFunc()()); //The Object
2.this绑定规则 默认绑定

最常用的函数调用类型:独立函数调用

function foo(){
    console.log(this.a);
}
var a = 2;
foo(); //2

上述代码中,this.a被解析成了全局变量a,这是因为函数调用时应用了this的默认绑定,因此this指向全局对象。虽然this的绑定规则完全取决于调用位置,但是只有foo()运行在非严格模式下,默认绑定才能绑定到全局对象;严格模式下与foo()调用位置无关,this会绑定到undefined

隐式绑定
function foo(){
    console.log(this.a);
}
var obj = {
    a:2,
    foo:foo
};
obj.foo(); //2

当foo函数被调用时,调用位置会使用obj上下文来引用函数,当函数引用有上下文对象时,隐式绑定规则会把函数调用中的this绑定到这个上下文对象,因此this.a和obj.a是一样的

隐式丢失

一个常见的this绑定问题就是隐式绑定的函数会丢失绑定对象,从而应用默认绑定规则

function foo(){
    console.log(this.a);
}
var obj = {
    a:2,
    foo:foo
};
var bar = obj.foo; //函数别名
var a = "global";
bar(); //"global"

虽然bar是obj.foo的一个引用,但是实际上它引用的是foo函数本身,因此此时的bar()其实是一个不带任何修饰的函数调用,因此应用了默认绑定

显式绑定

使用call,apply以及bind方法进行显示绑定

function foo(){
    console.log(this.a);
}
var obj = {
    a: 2
}
foo.call(obj); //2
new绑定

使用new来调用函数被称为构造函数调用,这种函数调用会自动执行如下操作:

创建一个全新的对象

这个新对象会被进行原型连接

这个新对象会绑定到函数调用的this

如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象

function foo(a){
    this.a = a;
}
var bar = new foo(2);
console.log(bar.a); //2
3.判断this

函数是否在new中调用(new绑定)?如果是的话,this绑定的是新创建的对象。var bar = new foo();

函数是否通过call,apply,bind(显示绑定)调用?如果是的话,this绑定的是指定的对象。var bar = foo.call(obj);

函数是否在某个上下文对象中调用(隐式绑定)?如果是的话,this绑定的是那个上下文对象。var bar = obj.foo();

如果都不是的话使用默认绑定。如果在严格模式下,就绑定到undefined,否则绑定到全局对象。var bar = foo();

参考文章:
深入理解JavaScript系列
JavaScript高级程序设计
你不知道的JavaScript

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/97483.html

相关文章

  • 基础巩固:JavaScript基础总结(基本概念篇)

    摘要:基础巩固基础总结使用已经好几年了,由于工作主要是做服务端开发,在工作中逐渐发现的使用范围原来越广泛。这里要注意,务必将基础部分掌握牢靠,磨刀不误砍柴功,只有将基础部分掌握并建立起系统的知识体系,在后面学习衍生的其他模式才能游刃有余。 基础巩固:JavaScript基础总结 使用JavaScript已经好几年了,由于工作主要是做服务端开发,在工作中逐渐发现JavaScript的使用范围原...

    YuboonaZhang 评论0 收藏0
  • 函数 - Javascript语法基础 - Javascript核心

    摘要:在定义函数的作用域外调用,得到的返回仍然是函数创建时所在的作用域的局部变量。这是因为所在的匿名函数的闭包中存放的是第一行的,而不是在循环中获得的的当前值。 原文: http://pij.robinqu.me/JavaScript_Core/JavaScript_Basics/Function.html 源代码: https://github.com/RobinQu/Pr...

    wuaiqiu 评论0 收藏0
  • 十步零基础JavaScript学习路径

    摘要:之前写过一篇天学通前端开发,内容主要讲的就是前端学习路径,今天再来写一篇零基础的学习路径,希望能帮编程零基础的前端爱好者指明方向。十框架三选一,零基础的初学者强烈推荐,如果是后台转前端推荐,如果技术型前端,推荐。 之前写过一篇26天学通前端开发,内容主要讲的就是前端学习路径,今天再来写一篇零基础的JavaScript学习路径,希望能帮编程零基础的前端爱好者指明方向。 一、开发环境和Ja...

    incredible 评论0 收藏0
  • Javascript学习总结 - JS基础系列一

    摘要:变量定义变量使用关键字变量名变量名可以任意取名,但要遵循命名规则变量必须使用字母下划线或者美元符开始。语法参数说明在消息对话框中要显示的文本返回值值。返回值点击确定按钮,文本框中的内容将作为函数返回值。 简述 本系列将持续更新Javascript基础部分的知识,谁都想掌握高端大气的技术,但是我觉得没有一个扎实的基础,我认为一切高阶技术对我来讲都是过眼云烟,要成为一名及格的前端工程师,必...

    Meils 评论0 收藏0
  • JavaScript基础知识

    摘要:用和包裹的内容,称为字符串。关系运算符用于进行比较的运算符。强制依赖于,非强制依赖于。使用场合全局环境构造函数对象的方法闭包闭包是指有权访问另一个函数作用域中的变量的函数。所有全局对象函数以及变量均自动成为对象的成员。 1 什么是JavaScript JavaScript一种直译式脚本语言,一种基于对象和事件驱动并具有安全性的客户端脚本语言;也是一种广泛应用客户端web开发的脚本语言。...

    Code4App 评论0 收藏0
  • 数组 - Javascript语法基础 - Javascript核心

    摘要:数组创建数组数组字面量使用构造函数数组本质上是所以要判断是不是数组,需要通过判断。数组长度使用属性获取元素的个数。例如函数的对象就是这样 原文: http://pij.robinqu.me/JavaScript_Core/JavaScript_Basics/Array.html 源代码: https://github.com/RobinQu/Programing-In-...

    molyzzx 评论0 收藏0

发表评论

0条评论

JackJiang

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<