JavaScript函数及call、apply、bind简单介绍

JavaScript函数

一、函数的定义

JS中函数的特点:

  • 一处定义,处处使用
  • 将函数定义在对象内部称为对象的方法
  • 函数也是一个对象,可以设置属性与方法
  • 每一次调用函数会产生一个this,谁调用的函数this就指向谁
  • 在函数中创建的变量为局部变量,在函数调用时声明,在函数调用结束后删除,且只能在函数内部使用,因此可以在不同函数中定义相同名称的变量
  • 可以多次调用一个函数为其传递不同参数获得不同结果
  • 函数可以用在表达式、计算式等其他地方,都可以得到相应的结果
1、函数声明语句

function name (参数){要执行的代码}

function fun1(){}//用函数声明语句定义一个函数
  • 函数不会执行,只有当调用时才会执行
  • 可以不带参数也可以没有返回值,甚至可以没有函数名
function fun1(){
	console.log('a');//未调用,不执行
}
function fun2(){
	console.log('b');
	return 'c';
}
console.log(fun2());//b c
//箭头函数或匿名函数可以不用写函数名
(()=>{
	console.log('d');
})()//d
2、函数表达式

var fun = function(参数){要执行的代码};

var fun = function(){}
  • 通过函数表达式创建的函数是一个匿名函数,存储于变量中
  • 函数调用时用:变量名()
var fun = function(){
	console.log(1);
};
fun();//1
3、Function构造函数

var name = new Function(“参数”, “要执行的代码”);

var fun = new Function("name","return 3");
console.log(fun());//3
  • 参数和代码的引号不能掉
  • 调用时通过:name()
  • 通过构造函数方式定义的函数不能进行递归
4、函数提升

当函数调用在前,定义在后时,就会产生函数提升,函数的提升是直接将整个函数整体提升到作用域的最开始位置,提升后的位置是在变量提升后的位置之后的,

类型 提升情况
变量 将变量定义提升到作用域最开始位置,并且只提升定义不提升赋值,但是变量先提升
函数 将函数整个提升到作用域最开始位置,相当于剪切复制
function foo() {
  console.log(a);//a(){}
  var a = 1;
  console.log(a);//1
  function a() {}
  console.log(a);//1
}
foo();

注意

  • 只有使用关键字声明的函数和变量才会提升
  • 局部变量中的隐式全局变量不会提升
5、自调用函数

在声明函数的同时,对函数进行调用:
( function(){} )()
必须将函数用括号包裹再跟一个调用的括号才可以进行自调用

(function fun(){
	console.log(2);
})()//2

当将一个函数赋值给一个变量,可以不用括号包裹函数,直接加一个执行的括号即可:

var fun = function(){
	console.log(1);
}();//1

二、函数调用

一般来说,声明的函数是不会执行的,当调用后才会执行,调用函数通常使用的方法是:functionName(),一般有以下几种调用方式:

1、函数调用模式

函数调用模式即普通调用模式:
functionName()

function fun(){
	console.log(1);
}
fun();//1
2、方法调用模式

方法调用模式即将函数定义为对象的方法,调用时:
ObjectName.functionName()

var obj = {
	name:'Tom',
	age:20,
	sleep:function(){
			console.log('sleep');
	}
}
obj.sleep();//sleep

注意:

  • 在使用方法调用模式时一定要保证this的指向正确,否则将会出现错误
3、构造函数调用模式

使用new关键字创建函数对象对构造函数进行调用:

function Person(name,age){
	this.name = name;
	this.age = age;
	this.say = function(){
		console.log(`${this.name}saying`);
	}
	console.log(this);
}
var person = new Person('Tom',30);//Person {name: "Tom", age: 30, say: ƒ}
console.log(person);//Person {name: "Tom", age: 30, say: ƒ}
person.say();//Tomsaying

注意:

  • new时this指向构造函数的实例化对象
  • 构造函数没有设置return时,或者return基本数值类型时,都默认返回this
  • 可以更改返回的this的指向,比如函数、对象、数组等
4、间接调用模式

使用call、apply、bind去间接调用定义的函数,返回值依然由函数return决定:

var obj = {};
var getSum = function(a, b) {
	console.log(this);
	return a + b;
}
// 间接调用模式: call/apply/bind
// call 和 apply可以改变函数内部this的指向,并且执行函数
console.log(getSum.call(obj, 10, 20));
console.log(getSum.apply(obj, [10, 20, 30]));
// bind改变了函数内部的this指向,但是没有执行函数
console.log(getSum.bind(obj, 10, 20)());

三、函数参数

名称 作用
形参 定义函数时设置的参数,用于接收实参的值,可以为形参设置初始值
实参 调用函数时传递的参数,用于赋值给形参,实参个数可以为任何个

注意:

  • 实参个数比形参多时,函数仅接收形参个数的实参,所有的实参将会保存在arguments类数组中
  • 实参个数比形参少时,未接收到实参的形参其值为undefined
  • 设置同名形参时,函数内部只能使用最后一个
function fun1(a,b,c){
	console.log(a,b,c);
}
fun1(1,2,3);//1,2,3
fun1(1,2);//1,2,undefined
fun1(1,2,3,4);//1,2,3
function fun2(a,a,a){
	console.log(a);
}
fun2(1,2,3);//3
1、arguments

函数的实参将会保存在arguments中,arguments是一个类数组,可以获取参数个数

function fun1(a,b,c){
	console.log(arguments);
	console.log(arguments.length);
}
fun1(1,2,3);//1,2,3   3
fun1(1,2,3,4,5);//1,2,3,4,5  5
fun1();// 0个 0

可以将arguments转换为数组:

function show(a,b,c) {
	console.log(arguments);
	for(var i = 0; i < arguments.length; i++) {
		console.log(arguments[i]);
	}
	// ES5将arguments转为数组
	var x = Array.prototype.slice.call(arguments);
	console.log(x);
	// ES6中方法
	var y = Array.from(arguments);
	console.log(y);
}
show("apple", "banana", "lemon");

显式参数就是在定义函数时列出的形参,函数可以不定义形参,没有形参的函数也可以传递实参,实参会存放在arguments中,这时就相当于函数有隐式参数

function fun4(){
	console.log(arguments);
}
fun4(1,2,3,4);//1,2,3,4
2、参数默认值

在定义函数参数时,给参数赋值

function fun(a,b = 1){
	console.log(a,b);
	for(var i = b; i < 10; i++){
			;
	}
	console.log(i);
}
fun(2);//a = 2,b = 1,i = 10

四、call、apply、bind

call、apply、bind都可以改变函数内部this的指向,

方法 特点
call 参数散列、更改后直接执行
apply 参数是数组、更改后直接执行
bind 参数散列,更改后不会直接执行,返回函数本身,需要再次调用
var Person = {
            username:'mei',
            say:function(){
                console.log(this.username);
            }
        }
var p1 = {username:'wang'};
var pp1 =Person.say.call(p1);//wang
        console.log(pp1);//undefined
        var pp2 = Person.say.apply(p1);//wang
        console.log(pp2); //undefined
        var pp4 = Person.say.bind(p1);
        console.log(pp4);//say方法
Person.say.bind(p1)();//wang

1、call的使用例子:functionname.call(this的指向对象)

var Person = {
     username:'mei',
     say:function(){
         console.log(this.username);
     }
}
Person.say();//mei
var p1 = {username:'wang'};
Person.say.call(p1);//wang
//将say劫持过来,用在call的第一个实参上
var p2 = {username:'zhang'};
 Person.say.call(p2);//zhang

2、apply的使用例子:functionname.apply(this的指向对象)

var Person = {
     username:'mei',
     say:function(){
         console.log(this.username);
     }
}
Person.say();//mei
var p3 = {username:'Tom'};
Person.say.apply(p3);

3、bind使用例子:bind:functionname.bind(this的指向对象),

var Person = {
     username:'mei',
     say:function(){
         console.log(this.username);
     }
}
Person.say();//mei
var p4 = {username:'Tony'};
Person.say.bind(p4);//没有输出
var pp4 = Person.say.bind(p4);//返回的是say方法
console.log(pp4);
Person.say.bind(p4)(); //调用后输出Tony

猜你喜欢

转载自blog.csdn.net/qq_42602282/article/details/106805991