天天看点

Js-w3school(2020.2.9)【js函数(展开部分)】

三十二、js函数(展开部分)

(一)函数定义

1.JavaScript 函数是通过 function 关键词定义的。您可以使用函数声明或函数表达式。

function functionName(parameters) {
   要执行的代码
}
           

被声明的函数不会直接执行。它们被“保存供稍后使用”,将在稍后执行,当它们被调用时。

由于函数声明不是可执行的语句,以分号结尾并不常见。

JavaScript 函数也可以使用表达式来定义。

函数表达式可以在变量中存储:

var x = function (a, b) {return a * b};
           

2.Function() 构造器

函数也可以通过名为 Function() 的内建 JavaScript 函数构造器来定义。

var myFunction = new Function("a", "b", "return a * b");
var x = myFunction(4, 3);
           

也可以不用

var myFunction = function (a, b) {return a * b};
var x = myFunction(4, 3);
           

3.函数提升

Hoisting 应用于变量声明和函数声明。

正因如此,JavaScript 函数能够在声明之前被调用

myFunction(5);
 function myFunction(y) {
     return y * y;
}
           

但是使用表达式定义的函数不会被提升。

3. 自调用函数

函数表达式可以作为“自调用”。

自调用表达式是自动被调用(开始)的,在不进行调用的情况下。

函数表达式会自动执行,假如表达式后面跟着 ()。

(function () {
    var x = "Hello!!";      //我会调用我自己
})();
           

4.函数可用作值

JavaScript 函数可被用作值,JavaScript 函数可用在表达式中

function myFunction(a, b) {
    return a * b;
}
var x = myFunction(4, 3) * 2;
           

5.函数是对象

JavaScript 中的 typeof 运算符会为函数返回 “function”。

JavaScript 函数都有属性和方法。

(1)arguments.length 会返回函数被调用时收到的参数数目:

function myFunction(a, b) {
    return arguments.length;
}
           

(2)toString() 方法以字符串返回函数:

function myFunction(a, b) {
    return a * b;
}
var txt = myFunction.toString();
//function myFunction(a, b) { return a * b; }
           

6.为创建新对象而设计的函数,被称为对象构造函数(对象构造器)

7. 箭头函数(es6)

箭头函数允许使用简短的语法来编写函数表达式。

您不需要 function 关键字、return 关键字和花括号。

形式:(参数)=>返回值

// ES5
var x = function(x, y) {
  return x * y;
}
// ES6
const x = (x, y) => x * y;
           

箭头函数没有自己的 this。它们不适合定义对象方法。

箭头函数未被提升。它们必须在使用前进行定义。

使用 const 比使用 var 更安全,因为函数表达式始终是常量值。

如果函数是单个语句,则只能省略 return 关键字和大括号。

const x = (x, y) => {
	var a=2;
	return (x**a)+(y**a);
};
document.getElementById("demo").innerHTML = x(1, 5);//26
           

IE11 或更早的版本不支持箭头函数。

(二)函数参数

1.函数参数定义

functionName(parameter1, parameter2, parameter3) {
    要执行的代码
}
           

函数参数(parameter)指的是在函数定义中列出的名称。

函数参数(argument)指的是传递到函数或由函数接收到的真实值。

2. 参数规则:无类型、无数量

JavaScript 函数定义不会为参数(parameter)规定数据类型。

JavaScript 函数不会对所传递的参数(argument)实行类型检查。

JavaScript 函数不会检查所接收参数(argument)的数量。

3. 如果调用参数时省略了参数(少于被声明的数量),则丢失的值被设置为:undefined。

4. 如果函数调用的参数太多(超过声明),则可以使用 arguments 对象(数组)来达到这些参数。

x = sumAll(1, 123, 500, 115, 44, 88);
function sumAll() {//求合函数
    var i, sum = 0;
    for (i = 0; i < arguments.length; i++) {
        sum += arguments[i];
    }
    return sum;
}
           

5.参数传值,对象传址

JavaScript 参数通过值传递:函数只知道值,而不是参数的位置。

如果函数改变了参数的值,它不会改变参数的原始值。

在 JavaScript 中,对象引用是值。

正因如此,对象的行为就像它们通过引用来传递:

如果函数改变了对象属性,它也改变了原始值。

(三)函数调用

1.在函数被定义时,函数内部的代码不会执行。在函数被调用时,函数内部的代码会被执行。

2.函数形式调用

function myFunction(a, b) {
    return a * b;
}
myFunction(10, 2);           // 将返回 20
           

2.作为方法调用

var myObject = {
    firstName:"Bill",
    lastName: "Gates",
    fullName: function () {
        return this.firstName + " " + this.lastName;
    }
}
myObject.fullName();         // 将返回 "Bill Gates"
           

3.函数构造器调用

如果函数调用的前面是 new 关键字,那么这是一个构造函数调用。

// 这是函数构造器:
function myFunction(arg1, arg2) {
    this.firstName = arg1;
    this.lastName  = arg2;
}
// 创建了一个新对象:
var x = new myFunction("Bill", "Gates");
x.firstName;                             // 会返回 "Bill"
           

(四)函数Call、Apply方法重用

1.call() 方法,您可以编写能够在不同对象上使用的方法。

call() 方法是预定义的 JavaScript 方法。它可以用来调用所有者对象作为参数的方法。通过 call(),您能够使用属于另一个对象的方法。

var person = {
  fullName: function(city, country) {
    return this.firstName + " " + this.lastName + "," + city + "," + country;
  }
}
var person1 = {
  firstName:"Bill",
  lastName: "Gates"
}
person.fullName.call(person1, "Seattle", "USA");
//理解为person1.fullName("Seattle", "USA")
           

2.通过 apply() 方法,您能够编写用于不同对象的方法。apply() 方法与 call() 方法非常相似,但是接受参数的方式不同

person.fullName.apply(person1, [“Oslo”, “Norway”]);

call()分别接收参数,apply()以数组形式接收参数

3.由于 JavaScript 数组没有 max() 方法,因此您可以应用 Math.max() 方法。

Math.max.apply(null, [1,2,3]); // 也会返回 3
Math.max.apply(Math, [1,2,3]); // 也会返回 3
Math.max.apply(" ", [1,2,3]); // 也会返回 3
Math.max.apply(0, [1,2,3]); // 也会返回 3
           

4.apply实现数组合并

Array.prototype.push.apply(arr1,arr2);
           

5.在 JavaScript 严格模式下,如果 apply() 方法的第一个参数不是对象,则它将成为被调用函数的所有者(对象)。非严格模式下,第一个参数值被指定为 null 或 undefined 时this值会自动替换为指向全局对象

(五)函数闭包

1.JavaScript 变量属于本地或全局作用域。全局变量能够通过闭包实现局部(私有)。

2. JavaScript 嵌套函数

所有函数都有权访问全局作用域。

事实上,在 JavaScript 中,所有函数都有权访问它们“上面”的作用域。

JavaScript 支持嵌套函数。嵌套函数可以访问其上的作用域。

function add() {
    var counter = 0;
    function plus() {counter += 1;}
    plus();     
    return counter; 
}
           

3.闭包

·闭包其实是在函数内部定义一个函数。

·闭包在使用的时候不会释放外部的引用,闭包函数内部的值会得到保留。

·闭包里面的匿名函数,读取变量的顺序,先读取本地变量,再读取父函数的局部变量。

·对于闭包外部无法引用它内部的变量,因此在函数内部创建的变量执行完后会立刻释放资源,不污染全局对象。

·闭包使用的时候要考虑到内存泄漏,因为不释放外部引用,但是合理的使用闭包是内存使用不是内存泄漏。

(1)自调用函数

var add = (function () {
    var counter = 0;
    return function () {return counter += 1;}
})();
add();
add();
add();
// 计数器目前是 3 
           

该函数在自定义时候首先执行一次并初始化counter为0。这个自调用函数只运行一次。它设置计数器为零(0),并返回函数表达式。

add 成为了函数,它能够访问父作用域中的计数器。

计数器被这个匿名函数的作用域保护,并且只能使用 add 函数来修改。

这被称为 JavaScript 闭包。它使函数拥有“私有”变量成为可能。

闭包指的是有权访问父作用域的函数,即使在父函数关闭之后。

(2)for循环案例

var arrays = [];

for (var i=0; i<3; i++) {
    arrays.push(function() {
        console.log('>>> ' + i); //all are 3
    });
}
           

上面的这段代码,刚看了代码一定会以为陆续打印出1,2,3,实际输出的是3,3,3,出现这种情况的原因是匿名函数保存的是引用

当for循环结束的时候,i已经变成3了,所以打印的时候变成3。出现这种情况的解决办法是利用闭包解决问题。

for (var i=0; i<3; i++) {
    (function(n) {
        tasks.push(function() {
            console.log('>>> ' + n);
        });
    })(i);
}
           

闭包里的匿名函数,读取变量的顺序,先读取本地变量,再读取父函数的局部变量,如果找不到去全局里面搜索,i作为局部变量存到闭包里面,所以调整后的代码可以能正常打印1,2,3。

for(var i=0;i<3;i++){
    !function(i){
        addEventListener('click',function(){
           alert(i);//1,2,3
        });
    }
}
           

!就是为了能省略一个字符……转化成表达式

// 这么写会报错,因为这是一个函数定义:
function() {}()

// 常见的(多了一对括号),调用匿名函数:
(function() {})()

// 但在前面加上一个布尔运算符(只多了一个感叹号),就是表达式了,将执行后面的代码,也就合法实现调用
!function() {}()
           

(3)封装

var person = function(){
    //变量作用域为函数内部,外部无法访问
    var name = "default";
       
    return {
       getName : function(){
           return name;
       },
       setName : function(newName){
           name = newName;
       }
    }
}();
print(person.name);//直接访问,结果为undefined
print(person.getName());
person.setName("kaola");
print(person.getName());
           

得到结果如下:

undefined

default

kaola

(4)

一道JS前端闭包面试题解析

求出程序输出

function fun(n,o){
    console.log(o);
    return {
        fun:function(m){
            return fun(m,n);
        }
    }
}
var a=fun(0);// undefined,因为第二个参数未定义
a.fun(1); //0
a.fun(2); //0
a.fun(3); //0
var b=fun(0).fun(1).fun(2).fun(3);//undefined 0 1 2,参数[0,] [1,0] [2,1] [3,2]
b.fun(1);//3
var c=fun(0).fun(1);// undefined 0,并且放置[1,0]
c.fun(2);//1,c创建时候传入[1,0],使n=1,(m=2,n=1),闭包调用o输出1
c.fun(3);//1,解释同上(m=3,n=1)
           

var a=...

开始的都是函数闭包创建fun类对象,之后的

a.fun()

是调用return{}里面的fun方法

本题详细分析见另帖https://blog.csdn.net/mus123/article/details/104235166

继续阅读