零基础快速掌握JavaScript函数简介及创建方式函数的本质与内部函数函数应用及闭包
Posted 志在四方csj
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了零基础快速掌握JavaScript函数简介及创建方式函数的本质与内部函数函数应用及闭包相关的知识,希望对你有一定的参考价值。
目录
一.函数介绍
函数允许我们封装一系列代码来完成特定任务。当想要完成某一任务时,只需要调用相应的代 码即可。方法(method)一般为定义在对象中的函数。浏览器为我们提供了很多内置方法,我们不需要编写代码,只需要调用方法即可完成特定功能。
二.函数的作用
功能的封装,直接调用,代码复用率提高(代码复用率是指相同代码被复用的概率,在某种程度上,代码复用率越高,代码越容易阅读,维护成本越低)
构建对象的模板(构造函数)
三.如何创建函数
1.创建函数的两种方式
//自定义函数
//方式一 函数声明
function test(){//function关键词 test函数名 ()参赛部分
//功能体
console.log(1)
}
//方式二 函数表达式 使用变量声明
var t1 = function () {
console.log(2)
}
test();//1
t1();//2
2.两种方式的区别
变量提升
//两种方式的区别:1变量提升
var c = 2;
function cook(a='番茄',b='鸡蛋'){//ab为形参 参数可以有默认值
//写参数时尽量把没有默认值的放在前面,这样在有值调用时会优先把值传给没有默认值的参数
//if(typeof a==undefined){}可以用判断语句来防止出现undefined的bug
var c = 1;
console.log(c);//1
//函数中会优先从局部寻找变量值,当局部没有时才会在全局寻找,都没有时就会报错
console.log(a+'炒'+b);
}
cook();//番茄炒鸡蛋
cook('萝卜');//萝卜炒鸡蛋
var c = 2;
function cook(a='番茄',b='鸡蛋'){
console.log(c);//undefined
//在函数中声明变量,变量名会被提升到局部顶端
//所以这时局部有变量c,就不会在全局寻找,所以值为undefined
var c = 1;
console.log(a+'炒'+b);
}
function cook(a='番茄',b='鸡蛋'){
var c = 1;
var res = a+'炒'+b;
//return设置函数返回值
//若函数在执行完毕后,需要一个结果,这个结果可以通过某个变量接收,那么就可以设置return,
//若我们只是想要执行一段逻辑,这段逻辑执行完毕即可,那就不需要设置return,
return res;//返回只有一个,并且返回后所有代码都无效了
console.log()//无效
}
var res = cook('apple');//res接收的就是函数的返回值
console.log(res);//apple炒鸡蛋
四.函数内部属性
1.arguments ☆☆☆
arguments是一个类数组对象,包含传入函数的所有参数
//函数内部属性
//arguments 是一个类数组对象,包含传入函数的所有参数
//类数组对象不是数组,可以当做数组使用,但不能使用数组的方法
//内部的下标跟数组相似,还需要有length,如果一个对象包含这2个特征,才被称为类数组对象
function add(a,b){
console.log(arguments);
}
add(1,2);//[Arguments] { '0': 1, '1': 2 }
function add(a,b){
console.log(arguments.length);
}
add(1,2);//2
function add(a,b){
console.log(arguments[0]);
}
add(1,2);//1
//在arguments中有一个属性 callee- 执行拥有该arguments的函数
//递归函数 在函数体内部调用自身 传入一个数10实现 10*9*8...*2*1
//使用递归函数时需要在某个时刻跳出循环
function digui(num) {
if (num<=1) {
return 1;
}else
return num * arguments.callee(num-1)
}
var res = digui(10);
console.log(res);
2.this
函数内部有this属性,指向函数所在环境,
//函数的内部有this属性,指向函数所在环境,
var a = 2;//不写var 就会存在global中
function add() {
console.log(this.a)//从Window中找a
}//this指向函数所在环境,这个函数在外部,即为全局
//当在浏览器中时指向window,当在node中时指向global
add();
var a = 2;
function add() {
console.log(this.a);//2
//函数定义在全局 从window中找a,不会从函数内部找,所以与下面这个新var的a没有关系
//当去掉this后console.log(a),这时会提升undefined,
//因为此时没有指向,所以会优先从内部寻找,就会发生变量提升,与上面的a=2就没有关系了
var a = 1;
console.log(a);//1
}
add();
var obj = {
a:3,
add(){//函数在对象内部定义,所以this指向该对象而非window或另一个函数
console.log(this.a);//3
}
}
总结:
函数或方法定义在全局,则可以在函数内部通过this调用这个函数或者在外部的方法
函数定义在对象内部,则可以在函数内通过this调用这个对象的内部属性或方法
3.IIFE函数
//IIFE函数 在js es5中的块级作用域(模拟的块级作用域)
//块级作用域是为了隔离作用域,防止污染全局
//es6块级作用域写法
var a = 1;
console.log(a);
{
var a = 2;
console.log(a);//相互独立,各打印各的
}
//es5写法
function test() {
var a = 3;
console.log(a);
}
test();//每次块级作用域声明完需要手动执行,而es6中是直接执行
(function () {//这个函数在声明后会立即调用
var a = 4;
console.log(a);
})()
4.作用域
函数作用域:函数内部声明的变量,在函数外部不能访问
全局作用域:函数外部声明的变量,在函数内部可以访问。
当函数嵌套,在这个时候,内部函数与外部函数的这个变量就组成了闭包。
在js中函数内部不存在块级作用域
五.函数调用
改变函数执行环境的三种方法:call()、apply()、bind()()
//如何改变函数的执行环境
name = 'xiaoming'
function sayName(a,b) {
console.log(this.name);
console.log(a+b);
}
var obj1 = {
name:'zhangsan',
}
var obj2 = {
name:'lis',
}
sayName();//xioaming NaN(非数值类型) nan是因为没有给ab传值
sayName.call(obj1);//将sayname方法的this指向变为obj1
sayName.call(obj2);//lis
sayName.apply(obj1);//zhangsan
sayName.bind(obj1)();//zhangsan
sayName(1,2);
sayName.call(obj1,1,2);
sayName.apply(obj1,[1,2]);
sayName.bind(obj1)(1,2);//返回的是一个方法
六.函数本质
函数本质上也是一种对象,拥有属性和方法
函数在内存中的表现形式:
函数当做特殊的对象,栈中保存函数名,堆中有两块区域:函数本身、函数原型,两块区域互相指向,你中有我我中有你,类似于Java的方法区。原型主要用在构造函数中
函数本质是一个对象,每个函数都有一个原型对象,通过"函数名.prototype"来访问该原型对象;原型对象中有个属性"constructor"指向函数
prototype:用来进行指向,创建对象时,若想让所创对象有方法,可以让其定义在prototype上
//函数的剖析(构造器函数)
function Animal(name,age) {
this.name=name;
this.age=age;
}
Animal.prototype.sayHello=function () {
//原型属性的功能:可以在原型属性中定义某些方法和属性,
//从而让创建的对象拥有这个方法或属性,如下面的对象xb
console.log(hello);
}
var d1 = new Animal('xb',2);
console.dir(Animal);
d1.sayHello();//hello
七.函数的应用
1.函数当作参数应用
函数做参数应用:由于函数名本身就是变量,所以函数可以当做值来使用(参数,返回值)
使用场景:定时器、Ajax的使用、数组遍历等
var arr = [1,2,3,4,5];
arr.forEach(function (v) {//v代表循环打印的一项
//在foreach函数内部要放一个函数作为参数使用,即回调函数
console.log(v);//1 2 3 4 5
})
//定时器
//bom方法 在node中无法测试
setTimeout(function () {
console.log('可以抢票了');
},2000);//2000毫秒=2秒 两秒后开始打印
2.函数作为返回值的情况
function add(a,b) {
//在这里可以返回函数、变量、对象
return function () {
return a+b;
}
}
var res=add(1,2);//返回了function
console.log(res());//3
八.闭包
闭包简单说就是创建一个函数,这个函数的函数体内部有一个变量,为了访问这个变量又在函数体内部创建了一个函数,以此来访问,即指有权访问另一个函数作用域中的变量的函数。
1.闭包创建方式
闭包的创建方式,就是在一个函数内部创建另外一个函数。
//闭包
//经典例子
function getName() {
var num=0;
return function () {
return num;
}
}
//console.log(num);//报错
//通常情况下我们无法直接访问局部变量,而通过这个例子可以实现外部访问函数内部变量的操作
var num = getName();
console.log(num());//0
2.闭包的特点
-函数的内部函数;-函数的内部引用外部的变量;-闭包不会被垃圾回收机制回收
function getNum() {
var num = 0;
return function () {
var n = 0;
console.log(++num);
//num引用了外部的num所以不会被销毁,所以逐次增加
console.log(++n);
//n在执行完被销毁,因为没有被引用,所以一直是1
}
}
var res = getNum();
//保持引用关系 res引用num,num又被内部引用,所以达到闭包效果不被销毁
res();// 1 1
res();// 2 1
res();// 3 1
getNum()();//这样写getNum将会被销毁,所以num一直为0
3.闭包的优缺点
优点:闭包可以调应函数内部变量,所以在封装有优势,若在实例中使用闭包,因为闭包可以持续拥有一个变量,这样就可以实现变量的累加、缓存等。
缺点:闭包只能取得包含函数中任何变量的最后一个值。
若闭包的变量不使用了,则因为其特殊的机制不被回收,从而造成内存损耗
解决方法:手动赋值为null
//闭包实例
for (var i = 1; i <=10; i++) {
setTimeout(function () {
console.log(i);
},1000)
}// 11 11 11 11...
//因为执行代码时,同步不会等异步,所以外部for循环先执行完毕,
//然后内部异步函数才开始执行,所以一直是11
//解决方法一: IIEF创建局部块
for(var j = 1; j <=10; j++){
(function (j) {//接受局部块变量
setTimeout(function () {
console.log(j);
})//每次执行for循环都会执行一个局部块
})(j)//传入局部块变量
}
//0 1 2 3 4...10
//解决方法二: 使用let
for (let i = 0; i <=10; i++) {
setTimeout(function () {
console.log(i);
},1000);
}
//0 1 2 3 4...10
//进阶版实例
function getNum(num) {
var res = [];
for (var i = 0; i < num; i++) {
res[i] = (function(i){//把i的值赋给数组res中下标为i的元素
return function(){
console.log(i);//从零开始输出i的值
}
})(i)
}
return res;//返回了包含多个函数的数组
}
var arr = getNum(3)
arr[0]();//0
arr[1]();//1
arr[2]();//2
以上是关于零基础快速掌握JavaScript函数简介及创建方式函数的本质与内部函数函数应用及闭包的主要内容,如果未能解决你的问题,请参考以下文章