zoukankan      html  css  js  c++  java
  • 函数表达式(JavaScript高程笔记)

    函数声明

    特点:函数声明提升(执行代码之前解析器会先读取函数声明,并使其在执行任何代码之前可用,意味着可以把函数声明放在调用语句之后)

    function functionName(arg0,arg1) {
    	//do something
    }
    

    函数表达式

    特点:无提升(须等到解析器执行到它所在的代码行才会真正被解析执行)
    1.最常见-匿名函数

    var functionName = function() {
    	//do something
    };
    

    2.命名函数表达式

    var functionName = (function f() {
    	//do something
    });
    

    一、递归

    1.arguments.callee
    一个指向正在执行的函数的指针,代替函数名

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

    2.命名函数表达式
    严格模式下arguments.callee报错,可用命名函数表达式代替

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

    二、闭包

    闭包是指有权访问另一个函数作用域中变量的函数
    创建闭包最常见的方式是在一个函数内部创建另外一个函数
    由于闭包会携带包含它的函数的作用域,因此过度使用闭包可能会导致内存占用过多
    闭包只能取得包含函数中任何变量的最后一个值(不是很理解)

    //错误栗子
    function createFunc() {
    	var result = new Array();
    	for(var i=0; i < 10; i++) {
    		result[i] = function() {
    			return i; //闭包,均返回i的最后一个值10
    		};
    	}
    	return result;
    }
    //正确栗子
    function createFunc() {
    	var result = new Array();
    	for(var i=0; i < 10; i++) {
    		result[i] = function(num){
    			return function() {
    				return num;
    			};
    		}(i); //立即执行,传递当前值给参数num
    	}
    	return result;
    }
    

    关于this:匿名函数的执行环境具有全局性,其this通常指向window
    IE9-:内存泄露-如果闭包的作用域链保存了一个HTML元素,则该元素无法被销毁

    //错误栗子
    function asignHandler() {
    	var element = document.getElementById('someElem');
    	element.onclick = function = () { //闭包
    		alert(element.id);
    	};
    }
    //正确栗子
    function asignHandler() {
    	var element = document.getElementById('someElem');
    	var id = element.id; //保存副本
    	element.onclick = function = () { //闭包
    		alert(id); //弹出副本
    	};
    	element = null; //解除引用,回收内存
    }
    

    三、模仿块级作用域

    JavaScript无块级作用域的概念

    //无块级作用域
    function outputNumbers(count) {
    	for(var i=0; i < count; i++) { //变量i在outputNumbers()的作用域中
    		alert(i);
    	}
    	alert(i); //在for外仍可访问变量i
    }
    //模仿块级作用域
    function outputNumbers(count) {
    	(function() {
    		//这里是块级作用域,闭包,可访问外部变量
    		for(var i=0; i < count; i++) {
    			alert(i);
    		}
    	})();
    	alert(i); //报错!
    }
    
    //运用:避免全局变量和函数
    //无指向匿名函数的引用,故能减少闭包占用的内存,只要函数执行完毕即可销毁其作用域链
    //把以下代码放在全局作用域中
    (function() {
    	var now = new Date(); //now为匿名函数的局部变量
    	if(now.getMonth() == 0 && now.getDate() == 1) {
    		alert('Happy new year!');
    	}
    })();
    

    四、私有变量与特权方法

    1.在构造函数中定义特权方法
    特点:每个实例都会创建同样一组新方法

    function Person(name) { //name为私有变量
    	//两个特权方法
    	this.getName() = function() {
    		return name;
    	};
    	this.setName() = function(value) {
    		name = value;
    	};
    }
    

    2.在私有作用域中定义私有变量和函数(静态私有变量)
    特点:私有变量和函数是每个实例共享的

    (function() {
    	var name = ''; //共享的,静态私有变量,
    	Person = function(value) {
    		name = value;
    	};
    	Person.prototype.getName = function() {
    		return name;
    	};
    	Person.prototype.setName = function(value) {
    		name = value; //在一个实例上调用setName()会影响所有实例的name
    	};
    })();
    

    3.模块模式
    为单例(只有一个实例的对象)创建私有变量和特权方法
    适用于需要对单例进行某些数据的初始化,同时又要维护其私有变量的情形

    var application = function() {
    	//私有变量和函数
    	var components = new Array();
    	//初始化
    	component.push(new BaseComponent()); //向数组添加了一个BaseComponent(无关紧要)的新实例
    	//特权方法
    	return {
    		getComponentCount : function() {
    			return components.length;
    		},
    		registerComponent : function(component) {
    			if(typeof component == 'object') {
    				components.push(component);
    			}
    		}
    	};
    }();
    

    4.增强模块模式(还没闹明白,明白后再补充)

    本书其它章节中关于函数的内容

    使用不带圆括号的函数名是访问函数指针,而非调用函数!

    1.return
    函数会在执行完return语句后停止并立即退出,因此,位于return语句之后的任何代码都永远不会执行
    return语句也可以无返回值,函数停止执行后将返回undefined

    2.参数
    函数内可通过arguments对象(函数的两个特殊对象之一)来访问参数数组(但arguments并非Array的实例),可与命名参数一起使用
    arguments与对应命名参数的内存空间是分别独立的,但它们的值会同步
    参数并非必须命名
    未传递值的命名参数将自动赋予undefined
    arguments.callee:是一个指针,指向拥有这个arguments对象的函数(消除紧密耦合)

    4.this(函数的两个特殊对象之一)
    引用的是函数据以执行的环境对象

    3.没有重载
    两个名字相同的函数,该名字只属于后定义的函数

    4.作为值的函数
    ECMAscript中的函数名本身就是变量,所以函数可以作为值来使用
    -可以像传递参数一样把一个函数传递给另一个函数(无圆括号)
    -可以将一个函数作为另一个函数的结果返回

    5.函数的内部属性
    arguments对象
    this对象
    caller属性:指向调用当前函数的函数,如果是在全局中调用当前函数则其值为null,可通过arguments.callee.caller实现更松散的耦合

    6.函数的属性和方法
    length属性:表示函数希望接受的命名参数的个数
    prototype属性:ECMAscript5不可枚举

    *设置this的值的方法,能扩充函数的作用域(非继承方法)

    -apply(作用域,[参数])
    -call(作用域,参数1,参数2,参数3)

    bind():创建一个实例,其this值绑定到bind()的参数

    *继承方法

    toLocaleString()、toString()、valueOf()
    以上均返回函数的代码

  • 相关阅读:
    著名的二分查找的BUG
    C/C++ static用法
    浅谈C++虚函数
    git备忘(长久更新)
    【经典问题】最大子串和
    水波纹效果
    博客迁址 xpeng.scorpionstudio.com
    终于,我们的新产品Fotor Slideshow Maker上线了!!
    分享一款浏览器扩展--美图搜索-图片搜索工具
    分享网页微信防撤回插件
  • 原文地址:https://www.cnblogs.com/qiuchen/p/4841915.html
Copyright © 2011-2022 走看看