【JS基础整理—No.02】函数

是对象,是一个引用数据类型

1.函数的本质

首先我们知道函数是引用类型数据,使用函数是为了提高代码的复用性

从本质上讲,函数是一种特殊对象(数组也是一种特殊的对象),函数内部数据私有。

👇这部分关于原型的问题先不细说,总之理解函数的本质是对象即可。

prototype:每个函数都有一个prototype属性,默认指向一个空对象(原型对象)

原型对象添加属性(一般添加方法)作用:函数的所有实例对象自动拥有原型中的属性(方法)

__proto__ 每个实例对象都有一个__proto__属性,该属性指向当前实例对象的原型对象(隐式原型对象)

//默认情况下,一个函数对应两块内存,一块是函数本身,一块是函数原型。一个函数都会与一个原型函数与之对应,函数中有个指针prototype指向原型对象,原型对象中有个constructor指向函数,你中有我,我中有你。
function foo(){}
foo.prototype.constructor===foo;        //true
console.log(foo.prototype);     //空对象,即原型对象

👇 函数内存图解:

 👇 原型继承图解

      紫色画笔部分

2.函数分类

本质上是完全一样的,唯一的区别在于调用方式。

  1. 普通函数【方法】
  2. 构造函数【类-在面向对象中学习】(为了区分将构造函数的函数名大写)

3.函数定义

函数存在变量提升,但需要注意两种方式下变量提升的区别(匿名函数不会被提升)

方式一--函数声明写法

function 函数名(形参){}
//-------------------------------------
function add(int a,int b){}     //❌
function add(a,b){}             //✅
//-------------------------------------
let result=add(1,2);
function add(a,b){
    return a+b
}                               //✅
//由于存在变量的提升,整个函数都被提升

 方式二--函数表达式写法

(即匿名函数,类似于普通赋值表达式)

bar();                      //bar is not defined
//-------------------------------------
add();
var add=function(a,b){
    return a+b
}                           //❌ add is not a function
//-------------------------------------
var add;                    //undefined
add();                      //❌报错
add=function(a,b){
    return a+b
}
//由于存在变量的提升,只提升了变量

4.函数调用

本质上任何函数的执行都是某个对象调用的

1.构造函数调用 通过new来调用

function Person(name,age){
    this.name=name;
    this.age=age;
}
var people1= new Person('kobe',42);

new 操作符 的相关 👉 【手写代码】new 操作符_Chailo的博客-优快云博客

2.普通函数

1.函数自调用

add(1,2);

2.回调函数

作为参数的函数。自己定义但没有调用,在一定条件下最后执行了。(同步没有回调,异步一定会回调)

例子🌰:DOM事件回调、定时器回调、Ajax回调函数

document.getElementById('btn').onclick=function(){
    console.log('事件回调');
}
​
setTimeourt(function(){
    console.log('定时器回调');
},1000)

3.强制绑定this的调用

👉【手写代码】call,apply,bind 的区别和实现原理_Chailo的博客-优快云博客

  1. 函数名.call(this,实参列表);
    add.call(this,1,2);
  2. 函数名.apply(this,实参数组)
    add.apply(this,[1,2]);
  3. 函数名.bind(this,实参列表);
    add.bind(this,1,2);
var obj = {
	name:'chailo',
	age:'12'
}
function fun(msg){
    this.msg = msg;
	console.log(this);
	console.log(msg);
}
fun();						//自调用 this-->window
fun.call(obj,'call传入参数');
fun.apply(obj,['apply传入参数']);
fun.bind(obj,'bind传入参数')();		
//bind绑定完this不会立即调用函数,而是将函数返回
//-->等价于
var fun2=fun.bind(obj,'bind传入参数');	
fun2();

4.IIFE 立即执行函数(匿名函数)

特点:①代码执行到函数位置,不会被提升        ②只执行一次        ③内部数据私有

IIFE的优点:

  1. 创建块级作用域(隐藏内部实现),避免了多人开发中全局变量和函数的命名冲突(不污染外部命名空间 );
  2. IIFE中定义的任何变量和函数,都会在执行结束时被销毁。因此可以减少闭包占用的内存问题

5.函数内部属性

只有在函数的执行过程中,内部属性才能被确定: argument,this,形参

1.arguments

保存函数的所有实参,是一个类数组对象

arguments.callee() 指向当前函数,常用于递归函数。但是在严格模式下无法使用。

👇 补充介绍一些类数组对象(我当时的学习笔记真的好细节,不愧是我!给个大拇指吧的👍🏾)

//类数组对象:访问方式和数组相似,但不是数组
var arr={
	"0"="terry";		//属性名加"":当出现特殊字符
	"1"="lerry";
	"2"="tom";
	length:3
}
Object.defineProperty(arr,'length',{
	configurable:true;
	enemerable:false;
	value:3
})						//使length迭代不出来,但是可以访问到
for(var k in arr){		//迭代
	console.log(k,arr[k]);
}
Array.isArray(arr);		//判断是否为数组 
var foo=function(a,b){
	console.log(a,b);						//只输出 1,2
	console.log(arguments);					//输出1,2,3,4,∵形参只能访问到部分,使用arguments可以访问所
	for(var i=0;i>arguments.length;i++){	//输出1,2,3,4
		console.log(arguments[i]);
	}
}
foo(1,2,3,4);

实际应用:

//n的阶乘,递归
function foo(num){
	if(num===1){
	return 1;
	}else{
		return foo(num-1)*num;
	}	
}
--------------使用arguments.callee(),但是在严格模式下受限制,无法使用
function bar(num){			
	if(num===1){
	return 1;
	}else{
		return arguments.callee(num-1)*num;	
		//∵函数名是可能改变的,两处函数名必须一样,所以可以使用arguments.callee() 
		//哈哈哈原来如此,当时的我好细节哦,要是没这标记我现在也不知道原因
	}	
}

2.this

内置变量,用于指向一个对象,this的指向与调用方式有关。

  1. 函数自调用this-->window
  2. 如果使用()调用函数,查看()前是否是函数名。如果是,查看函数名前是否有.,,有则指向.前面的那个对象没有则this指向全局对象
  3. 构造函数的this指向当前构造函数的实例对象 var fn1 = new fn(); this-->fn1
  4. 箭头函数的this看外层是否有函数。如果有,箭头函数的this=外层函数的this;如果没有,this--->window
  5. 使用call,apply,bind强制绑定this 👉【手写代码】call,apply,bind 的区别和实现原理【手写代码】call,apply,bind 的区别和实现原理

(箭头函数是一种特殊的函数形式后文会介绍,别急,宝~)

//举几个例子🌰-----------------------------
function foo(){
	console.log(this);			//全局 global
}	
//相当于
var foo=function(){}			//引用型,把一个匿名函数赋值给变量
//🌰-------------------------------------
var obj={
	name:"terry";
	foo:foo;
}
console.log(obj.foo===foo);		//true
obj.foo();						//this指向obj
//🌰-------------------------------------
var arr=[1,2,3,foo];
arr[3]();				
//🌰-------------------------------------
var a=1;
function test(){
	var a=2;
	function bar(){
		var a=3;
		console.log(this.a);	//global Undefined/window 1 
	}
	bar();						//∵this的取值跟调用方式有关,bar()的调用方式,函数名前没'.',指向全局变量。
}

6.箭头函数

是一种特殊的函数形式,多用来定义回调函数(使用场景)

this属性:箭头函数没有自己的this,箭头函数定义的时候处在的对象就是它的this,不是调用的时候决定的。(上面👆this那一部分有讲到!再来巩固一次)即,箭头函数的this看外层是否有函数。如果有,箭头函数的this=外层函数的this;如果没有,this--->window

//使用场景一及this指向
var foo=function(a,b){
	return a+b;
}
var f=fool(1,2);
console.log(f);			//结果:3
//写成箭头函数的形式-------------------------------------
var foo=(a,b)=>{			//简写:(a,b)=>a+b;
	console.log(this);		//结果:node下{}空对象,浏览器下window;∵没有包含该箭头函数的外部函数
    return a+b;
}
console.log(foo(1,2));		//结果:3 
-----------------------------------------------------------------------------------------
//使用场景二--做回调函数及this指向
var arr=[{name:"teery",age:12},{name:"teery",age:12}];
arr.forEach(function(item){			//遍历数组
	console.log(this);				//结果很多种,不是自己写的不讨论回调函数的this
	console.log(item);
})
//forEach+箭头函数-------------------------------------
function foo(){
	var arr=[1,2,3];
	arr.forEach((item)=>{
		console.log(this);			//就是foo的this
		console.log(item);
	})
}
//对于foo的this又和它的调用方式有关
foo();								//函数调用方式①:foo(实参列表);this为全局变量,∵前面没'.'
foo.call({name:"terry"});			//函数调用方式②:foo.call(this,实参列表);可指定this

7.函数应用

1.作为参数(回调函数---匿名内部类)

2.作为返回值(eg:闭包)

🎉over~


补充:匿名函数这部分  👉JavaScript匿名函数知多少

JavaScript并不是面向对象的,所以它不支持封装。但是在不支持封装的语言里同样可以实现封装。而实现的方法就是匿名函数。

因为没有指向匿名函数的引用。只要函数执行完毕,就可以立即销毁其作用域链了

javascript引擎规定,如果function关键字出现在行首,一律解释成函数声明语句;而函数声明后面是不能跟圆括号的(匿名函数是函数声明的一种)。然而,函数表达式的后面可以跟圆括号。所以可以将函数声明转换成函数表达式。

所以,解决方法就是不要让function出现在行首,让引擎将其理解成一个表达式 最常用的两种办法

(function(){ 
    console.log(123);
}()); 

(function(){
    console.log(123);
 })(); 

(function keith() {
    console.log(123);
})()

【面试题集—No.09】关于函数this的面试题_Chailo的博客-优快云博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值