首页 > 其他分享 >函数

函数

时间:2022-08-23 20:44:35浏览次数:40  
标签:function return 函数 var console log

创建方式

  • 函数表达式
var fun1 = function fn() {
	console.log(111);
};
console.dir(fun1);
fun1();
  • 函数声明式
function fun2() {
	console.log(222);
}
console.dir(fun2);
  • JS内置的Function构造函数创建
var fun3 = new Function('console.log(666); return 11;');
console.log(fun3());

调用函数

当调用函数时,如果没有return语句则返回undefined

  • 全局中的函数
function fun1() {
	console.log('这是全局中的函数');
}
fun1();
window.fun1();//this指向window
  • 对象中的函数
var obj = {
	fn: function () {
		console.log('这是对象中的函数');
	}
};
obj.fn();//this指向对象
  • 构造函数
function Person() {
	this.name = '大海';
}
var person1 = new Person();
  • 数组中的函数
var arr = [
	function () {
		console.log('这是数组中的函数');
		console.log(this);
	},
	666
];
arr[0]();
  • 函数被间接调用 call apply是函数的方法
function fn() {
	console.log('这个函数要被调用了 ---call ---apply', this);
}
fn.call();
fn.apply(obj);//改变this的指向

函数参数

参数的分类 显示参数:定义函数的时候的形参 隐式参数:arguments;

  • 显示参数:在声明/定义函数时,小括号内的元素叫做形参,在函数调用时,小括号内的元素叫做实参
function fun(params1, params2) {//params1, params2形参
	console.log(params1, params2);
	console.log(111);
	console.log(arguments); // 实参组成的类数组对象
}
fun(1,2);//1,2是实参
  • 隐式参数:当函数调用时,实参会放在arguments这个数组里

隐式参数arguments和显示参数存在对应关系(有实参的形参才会和arguments形成对应的关系 没有则不会)

function fun(a, b) {
	console.log(a, b); // 10 20
	a = 100;
	console.log(arguments); // [10, 20]
	arguments[0] = 9999;
	console.log(a);
}
fun(10, 20);

function fun1(a, b) {
	console.log(a, b); // 100 undefined
	console.log(arguments); // [100]
	arguments[1] = 300;
	console.log(b); // undefined
}
fun1(100);
  • 参数的个数

在函数内部使用arguments.length可以得到实参的个数,在函数为外部可以使用函数名.length得到实参个数

function fun(a, b, c, d) {
	console.log(arguments.length); // 实参的个数
	// for (k of arguments) {
	//     console.log(k);
	// }
	console.log(arguments.callee.length); // 形参的个数

	if (arguments.length == arguments.callee.length) {
		alert('形参的个数和实参的个数相等')
	} else {
		alert('形参的个数和实参的个数不相等')
	}
};
fun(11, 22, 33);

自调用函数

立即执行函数执行完毕后空间销毁,即使是函数的声明式也会被当作表达式进行执行,最后变成匿名函数,调用完没有人能再引用这个函数的地址

  • 函数表达式的自调用
var fun = function fn1() {
	var x = 'Hello,JavaScript1';
	console.log(x);
}();
console.log(fun); // undefined

var fun1 = (function fn2() {
	var x = 'Hello,JavaScript2';
	console.log(x);
}());

var fun2 = (function fn3() {
	var x = 'Hello,JavaScript3';
	console.log(x);
})();

fn1(); // fn1 is not defined
fn2(); // fn2 is not defined
fn3(); // fn3 is not defined
  • 函数声明式的自调用
// 语法检查不通过
/* function fun1() {
      var y = 'hello, Function!';
      console.log(y);
    }(); */

var res = (function fun2() {
	var y = 'hello, Function!';
	console.log(y);
	return 11;
}());

(function fun3() {
	var y = 'hello, Function!';
	console.log(y);
})();
fun2(); // fun2 is not defined
fun3(); // fun3 is not defined
// 函数自调用与逗号运算符练习1
function test(a, b, c, d) {
	console.log(a + b + c + d);
}

(1, 2, 3, 4);

function test(a, b, c, d) {
	console.log(a + b + c + d);
}

var res = (1, 2, 3, 4);
console.log(res);

// 函数自调用与逗号运算符练习2
var f = (
	function f() {
		return "1";
	},
	function g() {
		return 2;
	}
)(); // (function g() {return 2})()
console.log(typeof f);  // a:function  b:number   c:string

作用域链

  1. 函数产生(定义)的时候继承当前的作用域链
  2. 函数在被调用的时候 产生AO对象 放在自己作用域链的头部
  3. 函数内部在引用变量时,沿着自己的作用域链从头往后找
  4. 函数调用完毕,AO销毁 从自己作用域链头部去掉 里面的变量和函数也随之被销毁
function a() {
	function b() {
		var b = 234;
		aa = 0;
	}
	var aa = 123;
	b();
	console.log(aa);
}
var glob = 100;
a();
  • 下面代码来验证一下:
function a() {
    function b() {
        var b = 234;
        aa = 0;
    }
    var aa = 123;
    b();
    console.log(aa);
}
var glob = 100;
a();

答案:是同一个AO的引用。
再问,在上面的例子中,第7行b函数执行完之后,概念上是执行期上下文被销毁,而实际函数a和函数b的作用域变化应该是什么样的呢?
答案:

  • b函数执行完之后,自己的执行期上下文AO被干掉(销毁),即b.[[scope]]回到b被定义的状态。
  • 往下进行到第9行,a函数执行完后,b函数作用域b.[[scope]]直接被销毁;同时,a函数的执行期上下文AO被干掉(销毁),a.[[scope]]回到被定义的状态,a函数等待下一次被调用执行。

思考一下,下面函数执行的作用域链:

function a() {
    function b() {
        function c() {

        }
        c();
    }
    b();
}
a();

具体过程如下

a 定义:  a.[[scope]]    --> 	0:GO

a 执行:  a.[[scope]]    --> 	0:AO(a)
		      				 1:GO
		      				 
b 定义:  b.[[scope]]    --> 	0:AO(a)
		      				 1:GO
注意b执行了才会产生c的定义哈!!
b 执行:  b.[[scope]]    --> 	0:AO(b)
		      				 1:AO(a)
		      				 2:GO
		      				 
c 定义:  c.[[scope]]    --> 	0:AO(b)
		     				 1:AO(a)
		     				 2:GO
		     				 
c 执行: c.[[scope]]    -->   0:AO(c)
		      				 1:AO(b)
		      				 2:AO(a)
		      				 3:GO

现在再来看这句话,函数里边能访问函数外边的变量,但函数外边不能访问函数里边的变量;从上边的过程来看,在b中访问c中的局部变量,是不可能的,因为b.[[scope]]中不存在函数c的执行期上下文AO(c)。

关于作用域链,练习一下:

a=100;
function demo(e){
	function e() {	}
	arguments[0]=2;
	document.write(e+"<br>");
	if(a){
		var b=123;		//存在变量提升
		function c() {  //没有函数提升
      
        }
	}
	var c;
	a=10;
	var a;
	document.write(b+"<br>");
	f=123;
	document.write(c+"<br>");
	document.write(a+"<br>");
}
var a;
demo(1);
document.write(a+"<br>");
document.write(f+"<br>");

预解析

  1. 将函数提升为window属性
  2. 将函数中的var进行提升,值为undefined
  3. 对函数的形参进行提升,值为undefined
  4. 使用实参为形参赋值
  5. 将函数中的function进行提升
  6. 执行非var非function语句
function test(a, b) {
	console.log(a);
	c = 0;
	var c;
	a = 3;
	b = 2;
	console.log(b);
	function b() {}
	console.log(b);
}
test(1); // 1 2 2
function test(a, b) {
	console.log(a);
	console.log(b);
	var b = 234;
	console.log(b);
	a = 123;
	console.log(a);

	function a() {}
	var a;
	b = 234;
	var b = function () {};
	console.log(a);
	console.log(b);
}
test(1); // f a() {}  u   234    123   123   f () {}
function fn(a) {
	console.log(a);
	var a=123;
	console.log(a);
	function a() {}
	console.log(a);
	var b = function() {}
	console.log(b);
	function d() {}
}
fn(1);

1. 打印:function a() {},
2. 变量赋值(变量a声明已在预编译阶段完成)
AO{
	a:123,
		b:undefined,
			d:function d() {}	
}
3. 打印:123,
4. 打印:123(打印语句的上一行,函数a声明的提升已在预编译阶段完成),
		5. 变量赋值(变量b提升已完成)
AO{
	a:123,
	b:function() {},
	d:function d() {}
}
6. 打印:function() {},
7. 函数执行完成(函数d声明的提升已在预编译阶段完成)

闭包

官方对闭包的解释是:一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。
面试题:闭包是什么?
答:闭包是可访问上一层函数作用域里变量的函数,即便上一层函数已经关闭。

function a() {
	var num=100;
	function b() {
		num++;
		console.log(num);
	}
	return b;
}
var demo=a();
demo();
demo();
function fun(n, o) {
    console.log(o);
    return {
        fun: function(m) {
            return fun(m, n);
        }
    };
}

var a = fun(0);

a.fun(1);
a.fun(2);
a.fun(3);

var b = fun(0).fun(1).fun(2).fun(3);

var c = fun(0).fun(1);
c.fun(2);
c.fun(3);
function Person(name,age,sex){
    //var this={}
	var a=0;
	this.name=name;
	this.sex=sex;
	function sss(){
		a++;
		document.write(a);
	}
	this.say=sss;
    //return this;
}


var oPerson = new Person();
oPerson.say();
oPerson.say();
var oPerson1= new Person();
oPerson1.say();

闭包的特点

  1. 作为一个函数变量的一个引用,当函数返回时,其处于激活状态。
  2. 一个闭包就是当一个函数返回时,一个没有释放资源的栈区。

简单的说,Javascript允许使用内部函数---即函数定义和函数表达式位于另一个函数的函数体内。而且,这些内部函数可以访问它们所在的外部函数中声明的所有局部变量、参数和声明的其他内部函数。当其中一个这样的内部函数在包含它们的外部函数之外被调用时,就会形成闭包。
优点:可以访问局部变量。
缺点:局部变量一直占用内存,内存占用严重,还容易造成内存泄漏(内存被占用,剩余的内存变少,程序加载、处理速度变慢)。

闭包的几种写法

第一种:写在原型对象的方法上

// 第1种写法  
function Person() {

}

Person.prototype.type = "人类";
Person.prototype.getType = function() {
    return this.type;
}

var person = new Person();
console.log(person.getType()); // "人类"
// 继承性 (函数自己的原型对象上的属性被函数外的访问了)

第二种:内部函数语句访问外部函数的变量,将内部函数写在外部函数的return中

var Circle = function() { 
   //var this={}
   var obj = new Object();  //obj={}
   var a=100;
   obj.PI = 3.14159;  
     
   obj.area = function( r ) {  
       console.log(a); 
       return this.PI * r * r;  
       //this访问了外部函数的变量obj
   }  
   return obj;  
    //return this;
}  

var c = new Circle();  //{PI:3.14159,area:function(){……}}
alert( c.area( 1.0 ) );  
// 在函数的内部返回了一个对象 这个对象中有一个属性是函数 对象中的函数引用了外部函数的变量

第三种:给函数添加一些属性

function Circle(r) { 
	this.r = r; 
} 
Circle.PI = 3.14159; 
Circle.prototype.area = function() { 
	return Circle.PI * this.r * this.r; 
} 
var c = new Circle(1.0); 
alert(c.area()); //3.14159
// 函数身上的属性PI被外面访问了

第四种:通过属性创建写在对象的方法上

var Circle = {
    PI: 3.14159,
    area: function(r) {
        // var a = 'lll';
        return this.PI * r * r;
    }
};
alert(Circle.area(1.0));
// 外层能够通过调用函数访问函数里面的变量

第五种:通过全局变量赋值(类似于第二种写法的原理)

var demo;
function test(){
    var aaa=100;
    function b(){
        console.log(aaa)
    }
    return b;
}
demo=test()
test();
demo();

闭包的用途

1. 实现公有变量
eg:函数累加器

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

add();
add();
add();

你可以使用全局变量,函数设置计数器递增:

var counter = 0;
 
function add() {
   return counter += 1;
}
 
add();
add();
add();
 
// 计数器现在为 3

但问题来了,页面上的任何脚本都能改变计数器,即便没有调用 add() 函数。
这时我们需要闭包。

function fn(){
    var count=0;
    return function(){
        count+=1;
        return count;
    }
}
var add=fn()

 
add();
add();
add();

2. 可以做缓存
eg:eater

function eater() {
    var food = "";
    var obj = {
        eat: function() {
            console.log("i am eating" + food);
            food = "";
        },
        push: function(myfood) {
            food = myfood;
        }
    }
    return obj;
}
var eater1 = eater();
eater1.push("banana")
eater1.eat(); // i am eating banana


eater2 = eater();
eater2.push("apple");

eater2.eat();

eater2.eat();

暂时不必要理解”缓存“代表的是什么,先把这种闭包调用过程理解了就行(多个方法都可以在外部对同一个局部变量进行操作),以后讲到闭包高级应用的时候会再提出来的。
3. 可以实现封装,属性私有化

var person = function() {
    //变量作用域为函数内部,外部无法访问    
    var name = "default";

    return {
        getName: function() {
            return name;
        },
        setName: function(newName) {
            name = newName;
        }
    }
}();

console.log(person.name);
console.log(person.getName());
person.setName("abruzzi");
console.log(person.getName());

4. 模块化开发,防止污染全局变量

var a = (function(j) {
    return function() { console.log(j) }
    //模块内的函数变量不会被外部函数所污染,且执行完立即被销毁,不会浪费空间
}(i))

5. 实现类和继承

function Person() {
    var name = "default";

    return {
        getName: function() {
            return name;
        },
        setName: function(newName) {
            name = newName;
        }
    }
};
var p = new Person();
p.setName("Tom");
alert(p.getName());

var Jack = function() {};

Jack.prototype = new Person();

Jack.prototype.Say = function() {
    alert("Hello,my name is" + name);
};
var j = new Jack();
j.setName("Jack");
j.Say(); //Hello,my name is
alert(j.getName());

闭包的避免

看一下下main函数的执行结果:

function test() {
    var arr = [];
    for (var i = 0; i < 10; i++) {
        arr[i] = function() {
            document.write(i + "    ");

        }
    }
    return arr;
}
var myArr = test();
for (var j = 0; j < 10; j++) {
    myArr[j]();
}

上面函数将在界面上打印出10个10,数组myArr中保存的时test()执行后返回的数组arr,该数组存的是10个函数。
从第12行开始一个for循环,将数组myArr中的每一个函数都取出来调用一遍。
为了可以打印出0~9来,首先考虑内部函数自调用:

for (var i = 0; i < 10; i++) {
    arr[i] = function() {
        document.write(i + "    ");
        //上面的语句中并不存在赋值,只有在该函数被调用时,上面语句才会去寻找变量i的值
    }()
}

但这是立即执行,执行一遍就没有了。要想达到随时可以调用函数打印出0~9来,要怎么做?看看下面的代码:

for (var i = 0; i < 10; i++) {
    (function(j) {
        arr[j] = function() {
            document.write(j + "    ");
        }
    }(i))
}
  • 看得懂么?看不懂的话,往下看。
    • 循环执行10次,就有10个立即执行函数被执行;每个立即执行函数执行后其引用都会被销毁,所以10次循环对应的是10个立即函数。
    • 每个立即执行函数执行时,该立即执行函数中的形参j 都被实参i 所赋值。
    • 在10个立即执行函数执行后,数组arr中保存了10个函数 function(){ document.write(j+"    ");}。
    • 这10个函数对应的j 是10个立即执行函数的形参j,所以,数组arr中保存的10个函数中j 的值分别对应的是0~9。

这样才能随心所欲的打印出循环里的值0~9。
看一道阿里巴巴笔试题,练习一下:

<ul>
	<li>a</li>
	<li>a</li>
	<li>a</li>
	<li>a</li>
</ul>
<!-- 使用原生js给每个li绑定一个点击事件,点击对应的li,输出其顺序 -->

标签:function,return,函数,var,console,log
From: https://www.cnblogs.com/Kongqingzhi/p/16617727.html

相关文章

  • opencv-python常用函数
    一、安装pipinstallopencv-python二、图像读写cv2.imread(path,flag)返回值类型:np.ndarraycv2.imwrite(filename,image)三、常用绘图cv2.rectangle(image,......
  • 经常使用的一些函数及用法
    foriinrange(0,3,2)#i从0到3,每2个数取一次值input("pleaseinputtheage")#输入框,里面填提示信息int("123")#把......
  • python的控制结构和函数
    控制结构顺序结构程序秉承着从上到下从左到右的运行规则分支结构语法1:if条件:条件成立时,才会执行......
  • js函数节流(Throttle)
    在浏览器DOM事件里面,有一些事件会随着用户的操作不间断触发。比如:重新调整浏览器窗口大小(resize),浏览器页面滚动(scroll),鼠标移动(mousemove),输入框监听(oninput)等。也就是说......
  • 声明对象的两种方式 字面量式声明对象  构造函数声明对象
    1·字面量声明varobj={name:xiaohong};vararr=[1,2,3,45,6,7,98];2`构造函数声明对象varobj= newOjbect({......
  • Python中items()函数
    一、items()遍历字典中的元素遍历输出所有的key和value,以元组的形式1dict={'name':'python','define':'programminggrammer'}2foritemindict.items():#......
  • 【735】相关python函数用在算法题中提高效率
    Counter:用来计数使用fromcollectionsimportCounterfilter:用来表示满足一个函数的所有情况相关题目:260.只出现一次的数字III......
  • python函数装饰器篇
    python装饰器专栏1.了解装饰器1.函数装饰器,函数的简单功能的增强2.类装饰器,复杂功能的增强2.闭包1.简单讲就是在函数内在嵌套一个函数,就变成了闭包//闭包的......
  • 函数的返回值
    函数还有一个重要的功能就是返回结果。python中使用return关键字来退出函数,返回到函数被调用的地方继续往下执行。return可以将0个,1个,多个函数运算完的结果返回给函数被......
  • lambda函数
    简单来说,lambda函数用来定义简单的,能够在一行内表示的函数。语法格式如下:lambdaarg1,arg2,...:experssion案例:>>>f=lambdax,y:x+y#等价于>>>deff(x,y):......