最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • JavaScript语言基础(七)函数

    正文概述 掘金(Euphoria0901)   2021-08-19   719

    函数实际上是对象,每个函数都是Function的实例,而Function也有属性和方法,和其他引用类型一样。

    函数定义方式

    函数名就是指向函数对象的指针,而且不一定与函数本身紧密绑定。

    函数声明方式【通常】

    function sum(num1, num2){
    	return num1+num2;
    }
    

    函数定义最后没有分号。

    函数表达式

    let sum = function(num1, num2){
    	return num1+num2;
    };
    

    此时function关键字后面没有名称,这个函数通过sum变量来引用。这里函数末尾是有分号的,与任何变量初始化语句一样。

    “箭头函数”方式

    let sum = (num1, num2) => {
    	return num1+num2;
    };
    

    使用Function构造函数【不推荐使用】

    let sum = new Funtion("num1", "num2", "return num1+num2");
    

    可接收多个参数,最后一个函数始终是函数体,之前的参数都是函数的参数。

    箭头函数

    ES6新增使用胖箭头(=>)语法定义函数表达式的能力。任何使用函数表达式的地方,都可以使用箭头函数。

    非常适合嵌入函数的场景

    let ints = [1,2,3];
    
    console.log(ints.map(function(i){ return i+1; }));	// [2,3,4]
    console.log(ints.map((i) => { return i+1; }));	// [2,3,4]
    

    若只有一个参数,可以不用括号。如果没有参数或多个参数,则需使用括号

    箭头函数不能使用arguments、super和new.target,也不能用作构造函数,也没有prototype属性。

    函数名

    一个函数可以有多个名称,因为函数名就是指向函数的指针,所以它们跟其它包含对象指针的变量具有相同的行为。

    function sum(num1, num2){
    	return num1+num2;
    }
    console.log(sum(10, 10));	// 20
    
    let anothor = sum;
    console.log(another(10, 10));	// 20
    
    sum = null;
    console.log(sum(10, 10));	// 20
    

    使用不带括号的函数名会访问函数指针,而不会执行函数。 把sum设置为null之后,就切断了它与函数之间的关联,而another()还是可以照常调用。

    ES6的所有函数对象都暴露了一个只读的name属性,多数情况下,保存的是一个函数标识符,或者是一个字符串化的变量名。即使函数没有名称,也会如实显示成空字符串。 如果它使用Function构造函数创建的,则会标识成”anonymous“。

    function f1() {}
    let f2 = function(){};
    let f3 = () => {};
    
    console.log(f1.name);	// f1
    console.log(f2.name);	// f2
    console.log(f3.name);	// f3
    console.log((() => {}).name);	// (空字符串)
    console.log((new Function()).name);	// anonymous
    

    如果函数是一个设置函数、获取函数,或者使用bind()实例化,则标识符前面会添加前缀:

    function foo(){}
    console.log(foo.bind(null).name);	// bound foo
    
    let dog = {
    	years : 1,
    	get age(){
    		return this.years;
    	},
    	set age(newAge){
    		this.years = newAge;
    	}
    }
    let pd = Object.getOwnPropertyDescriptor(dog, 'age');
    console.log(pd.get.name);	// get age
    console.log(pd.set.name);	// set age
    

    理解参数

    ES函数的参数与大多数其它语言不同,既不关心传入参数的个数,也不关心这个参数的数据类型。

    主要因为ES函数的参数在内部表现为一个数组。事实上,在使用function定义(非箭头)函数时,可以在函数内部访问arguments对象,从中取得传进来的每个参数值。

    arguments对象是一个类数组对象(但不是Array的实例),可以使用中括号来访问其中的元素(如第一个参数为arguments[0]),若要确定传进来参数的个数,可以访问arguments.length属性。

    把函数重写成不声明参数也可以:

    function sayHi(){
    	console.log("Hello "+ arguments[0] +","+ argments[1]);
    }
    

    ES函数的参数并不是必须写出来的。 与其他语言不同,在ES中的命名参数不会创建让之后的调用必须匹配的函数签名,因为根本不存在验证命名参数的机制。

    arguments对象可以跟命名参数一起使用:

    function doAdd(num1, num2){
    	if(arguments.length === 1){
    		console.log(num1 + 10);	// 命名参数num1保存着与arguments[0]一样的值,使用谁都无所谓。
    	}else if(arguments.length === 2){
    		console.log(arguments[0] + num2);
    	}
    }
    

    arguments对象的值会与对应的命名参数同步。

    function doAdd(num1, num2){
    	arguments[1] = 10;
    	console.log(arguments[0] + num2);
    }
    

    上面例子,修改了arguments[1]也会修改num2的值。并不意味着访问同一个内存地址,它们在内存中还是分开的,只不过是保持同步而已。

    若只传一个参数,然后设置arguments[1]为某个值,则这个值不会反映到第二个命名参数,因为arguments对象的长度是传入的参数个数,而不是定义函数时的命名参数个数确定的。

    对于命名参数而言,若调用函数时没有传这个参数,则这个值为undefined。 严格模式下,若给arguments[1]赋值,不会影响到num2的值。在函数中尝试重写arguments对象会导致语法错误。(代码也不会执行)

    箭头函数中的参数 不能使用arguments关键字访问,而只能通过定义的命名参数访问

    可以在包装函数中把它提供给箭头函数:

    function foo(){
    	let bar = () =>{
    		console.log(arguments[0]);	// 5
    	};
    	bar();
    }
    foo(5);
    

    ES中的所有参数都按值传递的。不可能按引用传递参数。如果把对象作为参数传递,则传递的值就是这个对象的引用。

    没有重载

    ES函数没有签名,因为参数是由包含0或多个值的数组表示的。没有函数签名,自然也没有重载。

    若ES中定义了两个同名函数 ,则后面定义的会覆盖先定义的。 把函数名当作指针有助于理解为什么ES没有函数重载。

    let addNum = function(num){
    	return num+100;
    };
    addNum = function(num){
    	return num-100;
    };
    let result = addNum(200);	// 100
    

    默认参数值

    在ES5.1及之前,实现默认参数的一种常用方式就是检测某个参数是否等于undefined。 如果是则意味着没有传这个参数,则给它赋一个值。

    ES6之后,支持显式定义默认参数。只要在函数定义中的参数后面用=就可以为参数赋一个默认值。

    function makeKing(name = 'Henry'){
    	return 'King $(name) VIII';
    }
    

    给参数传undefined相当于没有传值,不过这样可以利用多个独立的默认值。

    在使用默认参数时,arguments对象的值不反映参数的默认值,只反映传给函数的参数。 与ES5严格模式一样,修改命名参数也不会影响arguments对象,始终以调用函数时传入的值为准。

    function makeKing(name = 'Henry'){
    	name = 'CLN';
    	return 'King $(arguments[0])';
    }
    console.log(makeKing());	// 'King undefined'
    console.log(makeKing('JK'));	// 'King JK'
    

    默认参数值并不限于原始值或对象类型,也可以使用调用函数返回的值。

    function makeKing(name = 'Henry', numerals = getNumerals()){
    	return 'King $(name) $(numerals)';
    }
    

    函数的默认参数只有在函数被调用时才会求值,不会在函数定义时求值。计算默认值的函数只有在调用函数但未传相应参数时才会被调用。

    箭头函数同样也可使用默认参数,只不过在只有一个参数时,必须使用括号。

    参数初始化顺序遵循”暂时性死区“规则,即前面定义的参数不能引用后面定义的。 参数也存在于自己的作用域中,它们不能引用函数体的作用域。

    // 调用时不传第二个参数会报错
    function makeKing(name = 'cln', numerals = defaultNum){
    	let defaultNum = 'ffzf';
    	return 'King $(name) $(numerals)';
    }
    

    参数扩展与收集

    ES6新增了扩展操作符,既可以用于调用函数时传参,也可以用于定义函数参数。

    扩展参数

    使用扩展操作符可以将数组直接传给函数:

    getSum(...values);
    

    因为数组长度已知,可以在扩展操作符的前面或后面再传递其它参数:

    countArg(-1, ...values);
    countArg(-1, ...values, 5);
    countArg(-1, ...values, ...[5,8,1]);
    

    arguments对象只是消费扩展操作符的一种方式,在普通函数和箭头函数中,也可以将扩展操作符用于命名参数,或者使用默认参数。

    收集参数

    可以使用扩展操作符把不同长度的独立参数组合成一个数组。

    收集参数的前面如果还有命名参数,则只会收集其余的参数。若没有就会得到空数组。因为收集参数的结果可变,所以只能把收集参数作为最后一个参数

    function collect(firstValue, ...values){
    	console.log(values);
    }
    collect();	// []
    collect(1);	// []
    collect(1,2);	// [2]
    collect(1,2,3);	// [2,3]
    

    箭头函数支持收集参数的定义方式。

    let getSum = (..values) => {
    	return values.reduce((x,y) => x+y, 0);
    }
    console.log(getSum(1,2,3));	// 6
    

    使用收集参数并不影响arguments对象,仍然反映调用时传给函数的参数。

    function getSum(...values){
    	console.log(arguments.length);	// 3
    	console.log(arguments);		// [1,2,3]
    	console.log(values);			// [1,2,3]
    }
    console.log(getSum(1,2,3));
    

    函数声明与函数表达式

    事实上,JavaScript引擎在加载数据时对它们是区别对待的。 JavaScript引擎在任何代码执行之前,会先读取函数声明,并在执行上下文中生成函数定义。

    console.log(sum(10,10));	// 20
    function sum (num1, num2){
    	return num1+num2;
    }
    

    函数声明会在任何代码执行之前先被读取并添加到执行上下文,这个过程叫做函数声明提升。

    而函数表达式必须等到代码执行到它那一行,才会在执行上下文中生成函数定义。

    console.log(sum(10,10));	// 会报错
    let sum =function(num1, num2){
    	return num1+num2;
    };
    

    上面代码出错,是因为这个函数定义包含在一个变量初始化语句中,而不是函数声明中。如果没有执行到这个语句,则执行上下文中没有函数的定义。 使用var关键字也会碰到同样的问题。

    函数作为值

    因为函数名在ES中就是变量,所以函数可以用在任何可以使用变量的地方。 意味着可以把函数作为参数传给另一个函数,而且还可以在一个函数中返回另一个函数。

    function callFunc(someFunc, someArg){
    	return somFunc(someArg);
    }
    function add10(num){
    	return num+10;
    }
    let result = callFunc(add10, 10);
    console.log(result);	// 20
    

    callFunc函数是通用的,第一个参数传入的是任何函数,始终返回调用作为第一个参数传入的函数的结果。 如果是访问函数而不是调用函数,就必须不带括号。 因此传给callFunc()必须是add10,而不是它的执行结果。

    从一个函数中返回另一个函数也是可以的。

    function compareFunc(propertyName){
    	return function(obj1, obj2){
    		let value1 = obj1[propertyName];
    		let value2 = obj2[propertyName];
    
    		if(value1 > value2){
    			return -1;
    		}else if(value1 < value2){
    			return 1;
    		}else{
    			return 0;
    		}
    	};
    }
    
    let data = {
    	{name : "jk", age : 24},
    	{name : "cln", age : 21}
    };
    data.sort(compareFunc("name"));
    console.log(data[0].name);	// cln
    data.sort(compareFunc("age"));
    console.log(data[0].name);	// cln
    

    函数内部

    在ES5中,函数内部存在两个特殊的对象:arguments和this。ES6又新增了new.target属性。

    arguments

    是一个类数组对象,包含调用参数时传入的所有参数。这个对象只有以function关键字定义函数(相对于使用箭头语法创建函数)时才会有。 还有一个属性callee,是一个指向arguments对象所在函数的指针。

    // 经典阶乘函数
    function factorial(num){
    	if(num <= 1){
    		return 1;
    	}else{
    		// return num * factorial(num-1);	// 紧密耦合
    		return num * arguments.callee(num-1);	// 可以让函数逻辑与函数名解耦
    	}
    }
    

    已经用arguments.callee代替了之前硬编码的factorial。无论函数叫什么名称,都可以正确引用函数。

    let trueFactoral = factorial;
    factorial = function(){
    	return 0;
    };
    console.log(trueFactorial(5));	// 120
    console.log(factorial(5));	// 0
    

    this

    它在标准函数和箭头函数中有不同的行为。

    在标准函数中,this引用的是把函数当成方法调用的上下文对象,通常称其为this值(在网页的全局上下文中调用函数时,this指向windows)。

    window.color = "red";
    let o = { color : "blue" };
    function sayColor(){
    	console.log(this.color);
    }
    sayColor();	// 'red',在全局上下文中调用,this指向window
    
    o.sayColor = sayColor;
    o.sayColor();	// 'blue',this指向o
    

    在箭头函数中,this引用的是定义箭头函数的上下文

    window.color = "red";
    let o = { color : "blue" };
    let sayColor = () => console.log(this.color);
    
    sayColor();	// 'red'
    
    o.sayColor = sayColor;
    o.sayColor();	// 'red',,在window上下文中定义的箭头函数,this指向window
    

    在事件回调或定时回调中调用某个函数时,this值指向的并没想要的对象。此时可以将回调函数写成箭头函数可以解决问题,因为箭头函数中的this会保留定义该函数时的上下文。

    function King(){
    	this.royaltyName = 'Henry';
    	// this 引用King的实例
    	setTimeout(() => console.log(this.royaltyName), 1000);
    }
    new King();	// Henry
    

    函数名只是保存指针的变量,因此全局定义的sayColor()函数和o.sayColor()是同一个函数,,只不过执行的上下文不同。

    caller

    ES5也会给函数对象上添加一个属性: caller,引用的是调用当前函数的函数,或者如果是全局作用域中调用的则为null。

    function outer(){
    	inner();
    }
    function inner(){
    	// console.log(inner.caller);	// inner.caller指向outer()
    	console.log(arguments.callee.caller);	// 降低耦合度
    }
    outer();
    

    在严格模式下访问arguments.callee会报错。

    ES5也定义了arguments.caller,严格模式下访问报错,同时不能给函数的caller属性赋值,否则会导致错误;非严格模式下始终是undefined。

    new.target

    ES6新增了检测函数是否使用new关键字调用的new.target属性。

    如果函数是正常调用,则new.target的值为undefined;

    若是使用new关键字调用的,则new.target将引用被调用的构造函数。

    function King(){
    	if(!new.target){
    		throw 'King must be instantiated using "new"';
    	}else{
    		console.log('King instantiated using "new"');
    	}
    }
    new King();	// King instantiated using "new"
    King();		// Error : King must be instantiated using "new"
    

    函数属性与方法

    每个函数都有两个属性:length和prototype,length属性保存函数定义的命名参数的个数。

    prototype是保存引用类型所有实例的地方,在ES5中,prototype属性是不可枚举的,因此使用for-in循环不会返回这个属性。

    函数有两个方法:apply()和call(),都会以指定的this值来调用函数,即会设置调用函数时函数体内this对象的值。

    apply()接收两个参数:函数体内this值和一个参数数组,第二个参数可以是Array的实例,或者arguments对象。

    sum.apply(this, arguments);
    sum.apply(this, [num1, num2]);
    

    在严格模式下,调用函数时如果没有指定上下文对象,则this值不会指向window。除非 使用apply()或call()把函数指定给一个对象,否则this值会变成undefined。

    call()与apply()作用一样,只是传参方式不同。第一个参数和apply()一样,即this值,而剩下的要传给被调用函数的参数是逐个传递的

    两种方法真正强大的地方,是控制函数调用上下文,即函数体内this值的能力。

    window.color = 'red';
    let o = { color : 'blue' };
    function sayColor(){
    	console.log(this.color);
    }
    sayColor();	// red,在全局上下文中调用,this指向window
    
    sayColor(this);	// red
    sayColor(window);	// red
    sayColor(o);	// blue
    

    使用apply()或call()的好处是可以将任意对象设置为任意函数的作用域。 前面的例子,为切换上下文需要先把sayColor()直接赋值为o的属性,然后再调用。 而在修改后的版本中,就不需要这一步操作了。

    ES5定义bind(),会创建一个新的函数实例,其this值会被绑定到传给bing()的对象。

    window.color = 'red';
    let o = { color : 'blue' };
    function sayColor(){
    	console.log(this.color);
    }
    let objSayColor = sayColor.bind(o);
    objSayColor();	// blue
    

    对函数而言,继承的方法toLocaleString()和toString()始终返回函数的代码,而valueof()返回函数本身。

    函数表达式

    创建一个函数再把它赋值给一个变量,这样创建的函数叫做匿名函数(又称兰姆达函数),因为function关键字后面没有标识符。

    理解函数声明与函数表达式的区别,关键是理解提升。

    if(condition){
    	function sayHi(){
    		console.log('Hi!');
    	}
    }else{
    	function sayHi(){
    		console.log('Yo!');
    	}
    }
    

    以上这段代码看起来正常,但是这种写法在ES并不是有效的写法,JS引擎会尝试将其纠正为适当的声明。 但是有些浏览器纠正这个问题的方式不一致。因此这种写法很危险,不要使用。

    let sayHi():
    if(condition){
    	sayHi = function(){
    		console.log('Hi!');
    	}
    }else{
    	sayHi = function(){
    		console.log('Yo!');
    	}
    }
    

    用函数表达式写法,如预期一样,根据condition的值为变量sayHi赋予相应的函数。

    递归

    在编写递归函数时,arguments.callee是引用当前函数的首选。

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

    在严格模式下运行的代码,是不能访问arguments.callee,因为访问会出错。 此时,可以使用命名函数表达式达到目的。

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

    即使把函数赋值给另一个变量,函数表达式的名称f也不变,因此递归调用不会有问题,这个模式在严格模式和非严格模式下都可以使用。

    尾调用优化

    ES6规范新增一项内存管理优化机制,让JS引擎在满足条件时可以重用栈帧。 这项优化非常适合“尾调用”,即外部函数的返回值是一个内部函数的返回值。

    尾调用优化的条件

    1. 代码在严格模式下执行;
    2. 外部函数的返回值是对尾调用函数的调用;
    3. 尾调用函数返回后不需要执行额外的逻辑;
    4. 尾调用函数不是引用外部函数作用域中自由变量的闭包。

    以下符合尾调用优化条件的例子:

    "use strict";
    
    // 有优化:栈帧销毁前执行参数计算
    function otherFunc(a,b){
    	return innerFunc(a+b);
    }
    
    // 有优化:初次返回值不涉及栈帧
    function otherFunc(a,b){
    	if(a<b){
    		return a;
    	}
    	return innerFunc(a+b);
    }
    
    // 有优化:两个内部函数都在尾部
    function otherFunc(condition){
    	return condition? innerFuncA() : innerFuncB();
    }
    

    尾调用优化的代码

    使用两个嵌套的函数,外部函数作为基础框架,内部函数执行递归:

    "use strict"
    
    // 基础框架,计算斐波那契数列的函数
    function fib(n){
    	return fibImpl(0,1,n);
    }
    
    // 执行递归
    function fibImpl(a,b,n){
    	if(n === 0){
    		return a;
    	}
    	return fibImpl(b, a+b, n-1);
    }
    

    代码重构之后,满足尾调用优化条件,再调用fib(1000)就不会对浏览器造成威胁了。

    闭包

    是指那些引用了另一个函数作用域中变量的函数,通常是在嵌套函数中实现的。

    // 前面举例的比较函数CompareFunc(propertyName)中
    let value1= obj1[propertyName];
    let value2= obj2[propertyName];
    

    在调用一个函数时,会为这个函数调用创建一个执行上下文,并创建一个作用域链。然后用 arguments和其它命名参数来初始化这个函数的活动对象。外部函数的活动对象是内部函数 作用域链上的第二个对象。这个作用域链一直向外串起了所有包含函数的活动对象,直到全局 执行上下文才终止。

    function compare(value1, value2){
    	if(value1 < value2){
    		return -1;
    	}else if(value1 > value2){
    		return 1;
    	}else{
    		return 0;
    	}
    }
    let result = compare(5,10);
    

    compare()是在全局上下文中调用的,第一次调用此函数时,会为它创建一个包含arguments、value1和 value2的活动对象,是其作用域链的第一个对象。而全局上下文的变量对象则是compare()作用域链上的第二个对象,其中包含 this、result和compare。

    函数执行时,每个执行上下文中都会有一个包含其中变量的对象。全局上下文中的叫做变量对象,会在代码执行期间始终存在。 而函数局部上下文中的叫做活动对象,只在函数执行期间存在。作用链其实是一个包含指针的列表,每个指针分别指向一个变量对象,但 物理上并不会包含相应的对象。

    闭包的作用域链中包含自己的一个变量对象,然后是包含函数的变量对象,直到全局上下文的变量对象。

    建议在使用闭包时要谨慎,仅在十分必要时使用。因为闭包会保留它们包含韩素华的作用域,所以比其他函数更占用内存。

    通常,函数作用域及其中的所有变量在函数执行完毕后都会被销毁。闭包在被函数返回之后,其作用域会一直保存在内存中,直到闭包被销毁。

    this对象

    如果在全局函数中调用,则this在非严格模式下等于window,在严格模式等于undefined。

    window.identity = 'The Window';
    let obj = {
    	identity : 'My Obj',
    	getIdentity(){
    		let that = this;
    		return function(){
    			that.identity;
    		};
    	}
    };
    console.log(obj.getIdentity()());	// 'My Obj'
    

    this和arguments都是不能直接在内部函数中访问的,如果想访问包含作用域中的arguments,则同样需要将其引用先保存 到闭包能访问的另一个变量中。

    立即调用的函数表达式

    立即调用的匿名函数,又称为立即调用的函数表达式。如果不在包含作用域中,将返回值赋给一个变量,则其包含的所有变量都会被销毁。

    (function(){
    	// 块级作用域
    }) ();
    

    使用ES块级作用域变量,让每次点击<div>都显示正确的索引。

    let divs = document.querySelectorAll('div');
    
    for(let i = 0; i< divs.length;++i){
            divs[i].addEventListener('click',function(){
    		console.log(i);	
    	});
    }
    

    在ES6中,若对for循环使用块级作用域变量关键字,在这里就是let,则循环会为每个循环创建 独立的变量,从而让每个点击处理程序都能引用特定的索引。

    私有变量

    任何定义在函数或块中的变量,都可以认为是私有的。

    私有变量包括函数参数、局部变量,以及函数内部定义的其它函数。

    特权方法是能够访问函数私有变量(及私有函数)的公有方法。在对象上两种方式创建特权方法:

    1. 在构造函数中实现
    function MyObj(){
    	let privateVariable = 30;
    	function privateFunc(){
    		return false;
    	}
    
    	// 特权方法,其实是一个闭包,具有访问构造函数中定义的所有变量和函数的能力
    	this.privateMethod = function(){
    		privateVariable++;
    		return privateFunc();  
    	}
    }
    

    静态私有变量

    1. 通过私有作用域定义私有变量和函数来实现,即使用原型模式通过自定义类型中实现
    (function(){
    	let privateVariable = 10;
    	function privateFunc(){
    		return false;
    	}
    
    	MyObj = function(){};	// 构造函数
    	MyObj.prototype.publicMethod = function(){	// 公有和特权方法,定义在原型上
    		privateVariable++;
    		return privateFunc();  
    	}
    })
    

    不使用关键字声明的变量,会创建在全局作用域中,MyObj变成了全局变量,注意在严格模式下给未声明的变量赋值会导致错误

    使用闭包和私有变量会导致作用域链变长,作用域链越长,则查找变量所需时间也越多。

    模块模式

    在一个单例对象上实现了相同的隔离和封装。单例对象就是只有一个实例的对象,JS通过对象字面量来创建单例对象的:

    let singleton = {
    	name : value,
    	method(){
    		// 方法代码
    	}
    };
    

    模块模式是在单例对象基础上加以扩展,使其通过作用域链来关联私有变量和特权方法。其样板代码如下:

    let singleton = {
    	let privateVariable = 10;
    	function privateFunc(){
    		return false;
    	}
    
    	// 特权/公有方法和属性, 对象字面量
    	return{
    		publicProperty : true,
    		
    		publicMethod(){
    			privateVariable++;
    			return privateFunc();  
    		}
    	};
    }();
    

    本质上,对象字面量定义了单例对象的公共接口。如果单例对象需要进行某种初始化,并且需要访问私有变量时,可以采取这种模式:

    let application = function(){
    	let components = new Array();		// 私有变量和私有函数
    	components.push(new BaseComponent());	// 初始化
    
    	// 公共接口,对象字面量
    	return{
    		getComponentCount(){
    			return components.length;
    		},
    		registerComponent(component){
    			if(typeof component == 'object'){
    				components.push(component);
    			}
    		}
    	};
    }();
    

    在模块模式中,单例对象作为一个模块,经过初始化可以包含某些私有数据,而这些数据可以通过其暴露的公共方法来访问。
    以这种方式创建的每个单例对象都是Object的实例,因为最终单例都由一个对象字面量来表示。

    模块增强模式

    在返回对象之前先对其进行增强。适合单例对象需要是某种特定类型的实例,但又必须给它添加额外属性或方法的场景。

    let singleton = function(){
    	let privateVariable = 10;
    	function privateFunc(){
    		return false;
    	}
    
    	let obj = new CustomType();	// 创建对象
    	obj.publicProperty = true;	// 添加特权/公有属性和方法
    	obj.publicMethod = function(){
    		privateVariable++;
    		return privateFunc();  
    	};
    	return obj;		// 返回对象
    }();
    

    若前一节的application对象必须是BaseComponent的实例,则可以使用以下代码创建它:

    let application = function(){
    	let components = new Array();		// 私有变量和私有函数
    	components.push(new BaseComponent());	// 初始化
    
    	let app = new BasComponent();
    	
    	// 公共接口
    	app.getComponentCount(){
    		return components.length;
    	};
    	app.registerComponent(component){
    		if(typeof component == 'object'){
    			components.push(component);
    		}
    	};
    	return app;	// 返回实例
    }();
    

    起源地下载网 » JavaScript语言基础(七)函数

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元