zoukankan      html  css  js  c++  java
  • [记录] JavaScript 中的函数

    函数:[JavaScript 教程]
    函数是一段可以反复调用的代码块。可以传递参数,不同的参数会返回不同的值。

    函数声明的三种方法:
    1. function 命令
    function 命令声明的代码块,就是一个函数。 function 命令后面是函数名,函数名后面是一对圆括号(), 里面可以传入参数。函数体放在大括号里面。

    function show(name) {
    	// 代码块...
    	console.log( name );
    }
    


    2. 函数表达式
    除了用 function 命令声明函数, 还可以采用变量赋值的写法。

    var show = function(name) { 
    	// 代码块...
    	console.log( name );
    };
    

    这种写法是将一个匿名函数赋值给变量。因为赋值语句的等号右侧只能放表达式,所有这个匿名函数称函数表达式。

    带名的函数表达式,函数名在函数体内有效,在函数体外部无效。

    var show = function abc() {
    	// 代码块...
        // 命名函数表达式 
    	// 每个函数都会有一个name的属性,这里的区别就在于 show.name  是 abc
    };
    

    注意: 函数作为表达式出现,则会忽略名称。函数表达式需要在语句结尾加上分号,表示语句结束。

    3. Function 构造函数

    Function 构造函数可以不使用new命令,返回结果完全一样。这种声明方式少人使用。
    var show = new Function(
    	"x",
    	"y",
    	"return x + y"
    );
    // 等同于
    function show(x, y) {
    	return x + y;
    }
    Function 构造函数的最后一个参数会被当作函数体,如果只有一个参数,该参数就是函数体。
    


    函数调用:
    有4种调用模式:函数调用模式、方法调用模式、构造器调用模式、间接调用模式;

    1. 函数调用模式

    function init(){
    	// 代码块...
    	// 非严格模式下,this指向全局对象window
    	// 'use strict'
    	// 严格模式下, this是undefined
    }
    init(); // window.init();
    可以通过 this 来判断当前是否是严格模式
    var strict = (function(){ return !this; }());
    


    2. 方法调用模式
    当一个函数被保存为对象的一个属性时,我们称它为一个方法。当一个方法被调用时,this被绑定到该对象。

    var myObject = {
    	value: 100,
    	init: function() {
    		console.log( this.value );
    	}
    }
    myObject.init(); // 100
    


    3. 构造器调用模式,用new关键字来新建一个函数对象的调用; (this指向被绑定到的构造函数实例上)

    var init = function (status) {
    	this.status = status;
    }
    init.prototype.getStatus = function () {
    	return this.status;	
    };
    var test = new init('构造函数');
    console.log( test.getStatus() ); // 构造函数; this指向test
    


    4. 间接调用模式 (this指向指定调用的对象)
    call()、apply()是函数的两个方法,可以用来间接地调用函数。

    var obj = {};
    function show(x, y) {
    	return x + y;
    }
    show.call(obj, 1, 2); //3 this => obj
    show.apply(obj, [1, 2]); //3 this => obj
    


    函数的重复声明:

    变量的重复声明是无用的,不会覆盖之前同一作用域声明的变量。
    但函数的重复声明会覆盖前面的声明的同名函数或同名变量。
    变量的重复声明变量无用

     
    var num = 10;
    var num;
    console.log( num ); // 10 
    

    覆盖同名变量

     
    var num;
    function num() {
        console.log(1);
    }
    num(); // 1
    

    覆盖同名函数

     
    function num() {
        console.log("A");
    }
    num(); // B
    
    function num() {
        console.log("B");
    }
    


    JavaScript 引擎将函数名视同变量名,所有采用 function 命令声明函数,都会被提升到代码头部。

    // 先调用
    show();
    // 后声明,由于"变量提升", 所以代码可以正常执行
    function show() { .. }
    
    采用赋值语句定义函数,先调用,再声明就会报错。
    show();
    var show = function() {}
    // TypeError: show is not a function
    等同以下形式
    var show;
    show();
    show = function() {};
    
    注意: 同时采用 function 命令 和 赋值语句 声明同一个函数,最后采用的总是赋值语句定义的。
    var show = function() {
    	console.log("A");	
    };
    function show() {
    	console.log("B");
    }
    show();  // A
    


    函数本身的作用域:
    函数执行时所在的作用域,是定义时的作用域,而不是调用时所在的作用域。

    函数的属性和方法:
    A. name 属性

    函数的 name 属性返回函数的名字
    function show() {}
    show.name; // show
    
    变量赋值定义的函数,name 属性返回变量名。
    var show = function () {
    	// 匿名函数表达式
    };
    show.name; // show
    
    var show = function abc() {
    	// 有名函数表达式
    }
    show.name; // abc
    


    B. length 属性 (函数形参的个数)

    function show(a, b){
    	// a,b 是函数的形参
    }
    show.length;  // 2
    


    C. toString() 方法: 返回一个字符串,内容是函数的源码;

    function show() {
    	// 注释
    	hide();
    }
    show.toString();
    结果:
    "function show() {
    	// 注释
    	hide();
    }"
    


    关于删除:

    function 命令声明函数无法删除,和变量声明一样
    function show() {
    	console.log("A");
    }
    delete show; // false
    show(); // A
    


    关于函数返回值:(return 有终止程序的作用)
    所有函数都有返回值,没有return语句时,默认返回内容是undefined,return语句不会阻止finally的执行

    function show() {
    	try{
    		return "A";
    	}catch(error) {
    		return "B";
    	}finally{
    		return "C";
    	}
    }
    show(); // C
    


    作为构造函数使用时,在调用之前前面加上了new,如果有return语句,返回值不是一个对象,则会忽略,则返回值是this(new出来的新对象);

    function show() { // 约定:如果一个函数要做为构造函数的话,首字母需大写。
    	this.num = 2;
    	return 1; // 返回的是一个基本类型,构造函数会忽略
    }
    var test = new show();
    console.log( test ); // { num: 2 }
    console.log( test.constructor ); // show(){ this.num = 2; return 1; }
    


    如果返回值是一个对象,则返回该对象。

    function show() {
    	this.num = 2;
    	return { num: 1 };
    }
    var test = new show();
    console.log( test ); // { num: 1 }
    console.log( test.constructor ); // Object(){ [native code] }
    


    关于函数的参数:
    函数运行的时候,有时需要提供外部数据,不同的外部数据会得到不同的结果,这种外部数据就叫参数。

    // 在函数声明时圆括号内的参数,叫形式参数 - 简称形参;

    function test(a, b, c){
    	// 形参 - 在函数内部相当于新定义的变量
    	// var a, b, c;
    	// 获取形参的长度: test.length;
    
    	// arguments 是函数的实参列表, 记录的是函数实际传入的参数
        // [1,2,3] arguments一个类数组 
    
    	// 形参个数 和  实参列表arguments一一对应,如有一方修改则都改,
        // 例外:当实参小于形参时,修改形参变量,对应的arguments为undefined 
    	
    	return a;
    }
    


    // 函数调用的时候掺入的参数,叫做实际参数 - 简称实参;

    test(1, 2); // 1; 实参比形参多, 则会忽略多传入的实参
    test(); // undefined; 形参比实参多, 内部用到则默认为undefined
    test( , 1); // 不能省略靠前的参数,如果要省略只有显示传入undefined
    // 报错 SyntaxError: Unexpected token ,(...)
    test(undefined, 1); // undefined
    


    同名形参: (在实际开发过程中肯定是不允出现的)
    A. 在非严格模式下,函数中可以出现同名形参,且只能访问最后出现的该名称的形参。

    function test(a, a, a) {
    	console.log(a);
    }
    test(1,2,3); // 3
    test(1,2); // undefined
    


    B. 在严格模式下,出现同名形参会抛出语法错误。

    function test(a,a,a){
    	'use strict';
    	return a;
    }
    test(1,2,3); // SyntaxError: Duplicate parameter name not allowed in this context
    


    以对象为参数:
    当一个函数的形参过多时,在调用的时候必须传入和形参对应的数据。

    function show(name, sex, age) {
    	// 在传入实参数是必须一一对应,name(名称),sex(性别),age(年龄)
    	console.log('姓名:' + name ',性别:' + sex + ', 年龄:' + age);
    }
    正常顺序
    show('jing', '女', '18'); // 姓名:jing,性别:女, 年龄:18
    错误顺序
    show('jing', '18', '女'); // 姓名:jing,性别:18, 年龄:女
    
    通过key/value对的形式来传入参数,参数的顺序就无关紧要了。
    function show(option) {
    	console.log(
            '姓名:' + option.name + 
            ',性别:' + option.sex +
            ', 年龄:' + option.age
        );
    }
    show({age: 18, name:'jing', sex: '女'}); // 姓名:jing,性别:女, 年龄:18
    


    以函数为参数:
    函数本身是一个对象,因此可以将函数作为另一个函数的参数,实现函数回调。

    function show( fn ) {
    	// 判断当前传入的参数类型是否是函数,如果是函数则立即执行;
    	if(typeof(fn) === 'function'){
    		fn(); // 执行回调
    	}
    }
    show(function(){
    	console.log('A');
    }); // A
    


    arguments对象:(函数内部的实参数列表)
    由于JavaScript允许函数有不定数目的参数,所以需要一种机制,可以在函数体内部读取所有参数。这是就arguments对象的由来。
    arguments对象包含了函数运行时的所有参数,arguments[下标]来获取,只有在函数体内才可以使用。

    function show() {
    	console.log(arguments[0]);
    	console.log(arguments[1]);
    }
    show("A", "B"); 
    // A
    // B
    


    当形参和实参的格式相同时,arguments对象的值和对应的形参的值保持同步

    function show(num1, num2) {
    	console.log( num1, arguments[0] ); // 1 1
    	arguments[0] = 7;
    	console.log( num1, arguments[0] ); // 7 7
    	num1 = 10;
    	console.log( num1, arguments[0] ); // 10 10
    }
    show(1);
    注意: 虽然形参和实参列表的值相同,但不是相同的命名空间。他们的命名空间是独立的,但值是同步的。
    


    在严格模式下,arguments对象的值和形参的值是 独立的

    function show(num1, num2) {
    	'use strict'
    	console.log( num1, arguments[0] ); // 1 1
    	arguments[0] = 7;
    	console.log( num1, arguments[0] ); // 1 7
    	num1 = 10;
    	console.log( num1, arguments[0] ); // 10 7
    }
    show(1);
    


    当形参并没有对应的实参时,arguments对象的值和形参的值并不对应

    function show(num1, num2) {
    	console.log( num1, arguments[0] ); // undefined, undefined
    	num1 = 10;
    	arguments[0] = 7;
    	console.log( num1, arguments[0] ); // 10 7
    }
    show();
    


    arguments对象的内部属性callee
    该属性是一个指针,指向拥有这个arguments对象的函数。

    var show = function() {
    	console.log( arguments.callee === show );
    }
    show(); // true
    可以通过 arguments.callee 达到调用函数自身的目的。
    注意:这个属性在严格模式下是禁用的。
    


    通过递归(函数调用自身)实现的阶乘函数

    function factorial(num) {
    	if( num <= 1 ){
    		return 1;
    	}else{
    		return num * factorial(num-1);
    	}
    }
    factorial(5); // 120
    


    使用arguments.callee消除函数解耦

    function factorial(num) {
    	if( num <= 1 ){
    		return 1;
    	}else{
    		return num * arguments.callee(num-1);
    	}
    }
    factorial(5); // 120
    

    闭包:
    理解闭包,必须理解变量作用域。
    JS的两种作用域: 全局作用域和函数作用域。函数内部可以直接读取全局变量。
    // 函数内部读取全局变量

    var num = 10;
    function show() {
    	cosnole.log( num );
    }
    show(); // 10
    


    函数外部无法读取函数内部声明的变量。

    function show() {
    	var num = 10;
    }
    console.log( num );
    // ReferenceError: num is not defined
    


    链式作用域,子对象可以向上寻找父对象的变量。
    // 函数内部嵌套函数,内部函数可以访问外部的变量

    function show(){
    	var num = 10;
    	function show2() {
    		console.log( num ); // 10
    	}
    	return show2;
    }
    


    函数 show 的返回值是函数 show2, 由于 show2 可以读取 show 的内部变量, 所以就可以在外部获得 show 的内部变量了。 show2 函数就是闭包, 能够读取其他函数内部变量的函数。
    只有函数内部的子函数才能读取内部变量,简单理解成,定义在一个函数内部的函数。
    闭包最大的特点,就是它可以记住诞生的环境,比如 show2 记住了它诞生的环境 show,所以从show2可以得到show的内部变量。 闭包就是将函数内部和函数外部连接起来的一座桥梁。

    闭包的最大的两个用处,1. 可以读取函数内部变量。 2. 让这些变量始终保持在内存中。

    function count(start) {
    	return function () {
    		return start++;
    	}
    }
    var init = count(5);
    init(); // 5;
    init(); // 6;
    init(); // 7
    通过闭包,start的状态被保留了,
    每一次调用都是在上一次调用的基础上进行计算。
    


    闭包的另一个用处: 是封装对象的私有属性和私有方法。

    function Person(name) {
    	var _age;
    	function setAge(n) {
    		_age = n;
    	}
    	function getAge() {
    		return _age;
    	}
    
    	return {
    		name: name,
    		getAge: getAge,
    		setAge: setAge
    	};
    }
    var p1 = Person('yuxi');
    p1.setAge(18);
    p1.getAge(); // 18
    函数 Person 的内部变量 _age, 通过闭包 getAge 和 setAge,变成了返回对象 p1 的私有变量。
    

    注意: 外层函数每次运行,都会生成一个新的闭包,而这个闭包又会保留外层函数的内部变量,所以会消耗很大内存。因此不能滥用闭包,否则会造成网页的性能问题。

  • 相关阅读:
    java 反编译和文档生成器
    java 导入自定义类
    hust 1010 最短循环节点
    KMP算法
    hiho 第119周 最大权闭合子图
    2007 Asia
    hiho 第1周 最长回文子串
    hiho 第118周 网络流四·最小路径覆盖
    hiho 第117周 二分图多重匹配,网络流解决
    成为一名JAVA高级工程师你需要学什么
  • 原文地址:https://www.cnblogs.com/yuxi2018/p/9549979.html
Copyright © 2011-2022 走看看