JS 07函数 函数介绍/声明/内部属性arguments,this/IIFE

本文介绍了JavaScript中的函数,包括函数的声明、作用,如代码复用、构造函数。详细讨论了函数内部的this属性、arguments对象以及它们在不同情况下的行为。此外,还阐述了IIFE(立即执行函数表达式)的概念及其在创建局部作用域和防止变量污染全局空间中的应用。
摘要由CSDN通过智能技术生成

函数

1.函数介绍

函数允许我们封装一系列代码来完成特定任务。当想要完成某一任务时,只需要调用相应的代码即可。方法(method)一般为定义在对象中的函数。

JavaScript 使用关键字 function 定义函数。

函数可以通过声明定义,也可以是一个表达式。

函数的作用:

功能的封装,直接调用,代码复用率提高

构建对象的模板(构造函数)

函数实际上是对象,每个函数都是Function类型的实例,并且都与其他引用类型一样具有属性和方法,由于函数是对象,因此函数名实际上也是一个指向函数对象的指针,不会与某个函数绑定。

2.函数声明

自定义函数
函数由function关键字声明,后面紧跟函数名,函数名后面为形参列表,列表后大括号括起来的内容为函数体。也可以将一个匿名函数(没有函数名的函数)赋值给一个函数变量,这种方式称为函数表达式。 解析器在向执行环境中加载数据时,会率先读取函数声明,并使其在执行任何代码之前可用;当执行器执行到函数表达式的代码的时候才会真正的解释执行。
表示方法:

函数声明
function 函数名(形参列表){
    //函数体
}
函数表达式
var 函数名 = function(形参列表){
    //函数体
}

这种形式看起来像常规的变量赋值,先创建一个匿名函数,然后赋值给变量函数名。我们知道在JS中如果要想使用一个变量,必须先给这个变量赋值,所以函数表达式也不例外,在调用之前,必须先给它赋值。否则会报错。

函数声明与var变量声明类似,会进行提升
function add(a,b){
  var result = a + b;
  return result;//返回值//返回执行的结果给被调用的
}
var total = add(1,2)

foo();//函数声明提升到代码的最前边,可以直接调用函数
function foo(){
  console.log("hell world");
  //return;
  //console.log("1");//return之后的语句不执行
  //如果没有返回的内容,则在写代码的时候不关注返回值
  //没有return:代码执行到大括号
}
//变量声明提升  变量声明提升到代码的前边,函数声明之后正常代码之前
console.log(a);   //undefined  这里不报错,因为后边有var a的声明。变量的声明进行提升到前边
var a = 'hello';  
console.log(a);   //'hello'
/**
 * 1.函数声明 
 * 函数体执行到return结束 没写return 执行到}结束 return 后面不写代码
 */
// function fn(形式参数){
// 	形式参数接受实际参数
// }
// 函数调用 函数名()
// fn(实际参数)
function add(a,b){
	console.log(a+b);
	// 函数内部不写返回值 返回undefined 
	return a + b
	// 一般情况下 写在return后面代码不生效  变量提升
	// var c = 10;
	// console.log(a + b );
}
var result = add(1,2);
console.log(result);


/**
 * 2.函数表达式 匿名函数赋值给一个变量
 */
var sum = function(a,b){
	console.log(a+b);
}
sum(1,2,3,4,5)
3.函数内部属性

只有在函数内部才能访问的属性。this也可以在函数外部进行使用。

arguments

ECMAScript函数的参数与大多数其他语言中的函数的参数有所不同,ECMAScript函数不介意传递参数的个数以及参数类型,这是因为函数的参数在函数内部是使用一个类数组对象来表示的。这个类数组对象就是arguments。

arguments是一个类数组对象,包含着传入函数中的所有参数。arguments主要用途是保存函数参数,但是这个对象还有一个名为callee的属性,该属性是一个指针,指向拥有这个arguments对象的函数。

//length声明时希望的参数的个数
function add(a,b){
  var result = a + b;
  return result;
}
console.log(add.length);//表示函数希望接受的命名参数的个数,即形参的个数。
Object.getOwnPropertyDescriptor(add,'length');
add(22,42,12,44);
/*参数:接收实参的快捷方式
	函数外部:实参保存的就是原始数据
	函数内部:arguments保存真正的实参
*/
/*arguments类数组对象
	arguments={
	“0”:1,
	“1”:33,
	“2”:”zs”
	};
	arguments[0],arguments[“1”]
*/
function add(a,b){
  console.log(arguments[0],arguments[1],arguments[2],arguments[3]);
  console.log(a+b);
}
add(10);
//10 undefined undefined undefined
//NaN
add(10,20);
//10 20 undefined undefined
//30
add(10,20,30);
//10 20 30 undefined
//30
add(10,20,30,40);
//10 20 30 40
//30

callee 属性是 arguments 对象的一个成员,仅当相关函数正在执行时才可用。

callee 属性的初始值就是正被执行的 Function 对象。

  • 使用arguments.callee()完成递归操作
// 实现匿名的递归函数
var sum = function (n) {
  if (1 == n) {
      return 1;
  } else {
      return n + arguments.callee(n - 1);
  }
}
console.log(sum(6));//输出结果:21
/**
 * auguments 属性 专门用来保存实际参数列表得类数组对象 
 * arguments内部属性 callee  仅当当前相关函数正在执行才可用
 */
function fn(a,b){
	console.log(a,b);
	console.log(arguments.length);
	console.log(arguments[3]); 
	console.log(arguments.callee,'arguments得属性')
	console.log(arguments.callee === fn)
	/**
	 * 面试题:如何将类数组对象转为数组对象 
	 * 1.Array.from() 将类数组转为数组
	 * 2.使用拓展运算符 
	 */
	// console.log(Array.from(arguments));
	// console.log([...arguments]);
}
fn(1,2,3,4,5);
// 形参个数就是函数得长度
console.log(fn.length);

/**
 * 递归 使用arguments.callee 实现递归 
 */
// function fc(n){
// 	if(n==1){
// 		return n = 1
// 	}
// 	return arguments.callee(n-1) * n
// }
// console.log(fc(10));


/**
 * 斐波那契数列 0 1 1 2 3 5 8 13 21 34 55 
 * 使用递归实现
 * 1 1  f(1) 1  f(0) 0 前两项相加之和等于第三项 
 * 0 0 	
 * 2 1	f(2) = f(1) + f(0) = 1 + 0 = 1
 * 3 2	f(3) = f(1) + f(2) = 1 + 1 =2
 */
// function fb(n){
// 	if(n<=1){
// 		return n
// 	}
// 	return arguments.callee(n-1) + arguments.callee(n-2) 
// }
// console.log(fb(10));
this

面向对象语言中 this 表示当前对象的一个引用。

但在 JavaScript 中 this 不是固定不变的,它会随着执行环境的改变而改变。

  • 在方法中,this 表示该方法所属的对象。
  • 如果单独使用,this 表示全局对象。
  • 在函数中,this 表示全局对象。
  • 在事件中,this 表示接收事件的元素。
  • 在显式函数绑定时,我们可以自己决定this的指向
实例
var person = {
  firstName: "LeBron",
  lastName : "James",
  id       : 8888,
  fullName : function() {
    return this.firstName + " " + this.lastName;
  }
};
方法中的 this

在对象方法中, this 指向调用它所在方法的对象。

在上面一个实例中,this 表示 person 对象。

fullName 方法所属的对象就是 person。

fullName : function() {  return this.firstName + " " + this.lastName; }
单独使用 this

单独使用 this,则它指向全局对象。

在浏览器中,window 就是该全局对象为 [object Window]:

在node中,指向的是一个{}

var x = this;
函数中使用 this(默认)

在函数中,函数的所属者默认绑定到 this 上。

在浏览器中,window 就是该全局对象为 [object Window]:

在node中,指向的就是global对象

function myFunction() {  return this; }
事件中的 this

在 HTML 事件句柄中,this 指向了接收事件的 HTML 元素:

<button οnclick="this.style.display='none'"> 点我后我就消失了 </button>
显式函数绑定

在 JavaScript 中函数也是对象,对象则有方法,apply 和 call 就是函数对象的方法。这两个方法异常强大,他们允许切换函数执行的上下文环境(context),即 this 绑定的对象。

在下面实例中,当我们使用 person2 作为参数来调用 person1.fullName 方法时, this 将指向 person2, 即便它是 person1 的方法:

var person1 = {
  fullName: function () {
    return this.firstName + " " + this.lastName;
  }
}
var person2 = {
  firstName: "Zach",
  lastName: "Muyi",
}
var name = person1.fullName.call(person2);  // 返回 "Zach Muyi"
console.log(name);


/**
 * this 是函数赖以执行得环境对象
 * 	1.关注this被谁拥有  2.关注拥有this方法被谁调用  this就指向谁
 * 1.单独使用this  this在nodejs指向当前模块 this在浏览器指向全局对象window
 * 2.函数内部使用this  this指向全局对象 global window
 * 3.方法中使用this  this指向拥有该方法得调用者 
 * 4.在事件中使用this  this指向接收事件得元素
 * 5.显示函数绑定时,可以更改this指向
 */
// console.log(this);//{} 当前模块 
// function foo(){
// 	console.log(this);//global obejct 
// }
// foo()
name = 'larry';
var obj = {
	name:'terry',
	sayName:function(){
		console.log(this);
		console.log(this===obj);
		console.log(this===globalThis);
		console.log(this.name);
	}
}
// obj.sayName();//this -->obj对象 this===obj true this.name--->terry
var x = obj.sayName;
x();//this --global this===obj false this===globalThis true 


var obj1 = {
	name:'terry'
}
var obj2 = {
	sayName:function(){
		console.log(this.name,this)
	}
}
// call 方法修改this指向  参数:this执行得环境对象 
obj2.sayName.call(obj1);
4.IIFE

​ IIFE: Immediately Invoked Function Expression,意为立即调用的函数表达式,也就是说,声明函数的同时立即调用这个函数。

作用
  • 页面加载完成后只执行一次的设置函数。
  • 将设置函数中的变量包裹在局部作用域中,不会泄露成全局变量。
对比一下,这是不采用IIFE时的函数声明和函数调用:
function foo(){
  var a = 10;
  console.log(a);
}
foo();
下面是IIFE形式的函数调用:
(function foo(){
  var a = 10;
  console.log(a);
})();

函数的声明和IIFE的区别在于,在函数的声明中,我们首先看到的是function关键字,而IIFE我们首先看到的是左边的(。也就是说,使用一对()将函数的声明括起来,使得JS编译器不再认为这是一个函数声明,而是一个IIFE,即需要立刻执行声明的函数。两者达到的目的是相同的,都是声明了一个函数foo并且随后调用函数foo。

IIFE的写法

1.对返回结果不进行处理

(function(形参){
	函数体内容
})(实参);

2.对返回结果不进行处理

(function(形参){
	函数体内容
}(实参));

3.返回的是一个布尔值,然后进行取反

!function(形参){
	函数体内容
}(实参)

4.对于数字返回的是原来的结果,非数字返回NaN

+function(形参){
	函数体内容
}(实参)

5.对于数字返回的是正负符号相反,非数字返回NaN

-function(形参){
	函数体内容
}(实参)

6.对于数字返回的是正负符号相反再减1,非数字返回-1

~function(形参){
	函数体内容
}(实参)

7.返回的结果是undefined

void function(形参){
	函数体内容
}(实参)
IIFE的基本使用
// 就像其它任何函数一样,一个立即执行函数也能返回值并且可以赋值给其它变量。
var sum = (function (a,b) {
  return a + b;
}(1,2))
console.log(sum);
案列-经典面试题-IIFE
for (var i = 0; i < 6; i++) {
  function output() {
    console.log(i); // 为什么输出的是6,而不是0,1,2,3,4,5
    // 因为输出的 i 是全局作用域的,当循环结束后 i 的值是 6,所以输出的 i 就是6。
  }
}
output()
for (var i = 0; i < 6; i++) {
  (function (j) {
    console.log(j); //0,1,2,3,4,5
  })(i)
  // 因为 JS 中调用函数传递参数都是值传递 ,所以当立即执行函数执行时,首先会把参数 i 的值复制一份,然后再创建函数作用域来执行函数,循环5次就会创建5个作用域,所以每个输出访问的都是不同作用域的 i 的值 。
}
为什么需要IIFE?

如果只是为了立即执行一个函数,显然IIFE所带来的好处有限。实际上,IIFE的出现是为了弥补JS(ES5)在作用域方面的缺陷:JS只有全局作用域(global scope)函数作用域(function scope),从ES6开始才有块级作用域(block scope)。对比现在流行的其他面向对象的语言可以看出,JS在访问控制这方面是多么的脆弱!那么如何实现作用域的隔离呢?在JS中,只有function才能实现作用域隔离,因此如果要将一段代码中的变量、函数等的定义隔离出来,只能将这段代码封装到一个函数中。在我们通常的理解中,将代码封装到函数中的目的是为了复用。在JS中,当然声明函数的目的在大多数情况下也是为了复用,但是JS迫于作用域控制手段的贫乏,我们也经常看到只使用一次的函数:这通常的目的是为了隔离作用域了!既然只使用一次,那么立即执行好了!既然只使用一次,函数的名字也省掉了!这就是IIFE的由来。

/**
 * 立即执行函数  声明得时候就调用 
 * 特点:1.页面加载得时候会立即执行一次 
 * 2.拥有局部作用域 变量不会泄露
 */
// var a = function(b,c){
// 	return b + c
// }
// console.log(a(1,2));;
/**
 * 立即执行函数 (匿名函数(形式参数){})(实际参数)
 */
// (function(a,b){
// 	console.log('我是立即执行函数',a,b)
// })(1,2)

// var res = ((function(a){
// 	console.log('我是立即执行函数2',a)
// })('hello'));
// console.log(res);
var i = 0;
for(;i<6;i++){
	function foo(){
		console.log(i)
	}
}
foo();//6

for(var i = 0;i<6;i++){
	(function(j){
		console.log(j)
	})(i);//0 1 2 3 4 5
}


var arr = [];
// 循环遍历 往数组添加元素 
for(var i=0;i<5;i++){
	// 闭包 函数内部使用函数外部变量  解决作用域问题
	arr[i] = (function(j){
		return function(){
			console.log(j)
		}
	})(i);// 0 1 2 3 4 
}
arr[4]();
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值