Js高级-函数的理解

一函数的理解:

  1. 理解

函数也是对象
函数是特殊的对象,因为函数具备行为,通过调用可以执行内部语句

  1. 定义函数方式

函数声明式:function fun(){}
函数表达式: var fun2 = function(){}

  1. 调用函数方式

test() 直接加括号调用
new test() 以构造函数形式调用
obj.test() 对象.方法形式调用

   var obj = {
            fun:function(){};
            }
   obj.fun();

test.call/apply(obj) 以修改this指向的形式调用
call || apply 用以修改this指向的(强制绑定this),自动调用修改完this指向的函数
语法:test.call(指定的this指向对象,参数)

   var obj2 = {
       name:"kobe"
};
   function test(num,num2) {
   console.log(this,num,num2)
};
   test.call(obj2,123,234)
   test.apply(obj2,[123,234])//把this原本指向window修改为指向obj2,并自动调用函数输出结果
  1. 为什么要设计函数?

1简化代码的编写,减少重复的代码
2功能点明确,复用
3隔离变量,减少命名污染

二.回调函数的理解

  1. 什么函数才是回调函数?

你定义的 你没有直接调用 但最终它执行了(在特定条件或时刻)

  1. 常见的回调函数?

DOM事件函数
定时器函数
ajax回调函数
生命周期回调函数

三.匿名函数

  1. 理解

全称: Immediately-Invoked Function Expression 立即调用函数表达式
别名: 匿名函数自调用

  1. 作用

隐藏内部实现
不污染外部命名空间

  (function () {
     var a = 123;
     console.log('立即执行函数调用');
  }())

四.函数中的this
1.理解this

一个关键字
一个内置的引用变量

2.this的指向问题

函数this不是函数定义的时候决定的
函数this指向谁,看如何调用当前的函数

3.this指向的分类

test() 函数自调用:window
new test() 构造函数:当前构造函数的实例对象
obj.test() 对象.方法():对象本身
test.call/apply(指定的对象):指定的对象

五.原型
1.什么是原型对象

每个函数都有一个prototype属性,该属性指向的是原型对象(显示原型对象,开发者可以直接操作)
每个实例对象身上都有一个_ proto _属性,该属性指向也是原型对象(隐式原型对象,开发者不能直接操作)
构造函数的显示原型对象===当前构造函数实例对象的隐士原型对象
原型对象本身就是一个普通的实例对象,并且初始化的时候是空的对象,后期添加的constructor属性,该属性指向当前的构造函数本身

  function Persion(name,age){
         this.name = name;
         this.age = age;
         //添加一个显示名字的方法
         this.showName = function(){
         console.log(this.name);
         }
}
  var Persion1 = new persion"kobe"43var Persion2 = new persion"kobe2"43var Persion3 = new persion"kobe3"43)
  console.log(Persion1)
  Persion1.showName();
  Persion2.showName();
  Persion3.showName();

remark:尽管name和age都会不一样,但是显示名字的showName这个方法行为都是一样的,现在3个对象都定义了3个方法,占用3个内存空间,无形会浪费内存空间,如果能把这个showName方法放进一个公共的地方,好比一个工具箱,需要时候调用,那么这个公共地方就是原型对象!

  function Persion(name,age){
         this.name = name;
         this.age = age;     
}//将showName这个方法放进公共地方,即原型对象
  Persion.prototype.showName = function(){
  console.log(this.name);
  }
  console.log(Persion.prototype);
  var Persion1 = new persion"kobe"43var Persion2 = new persion"kobe2"43var Persion3 = new persion"kobe3"43)
  Persion1.showName();
  Persion2.showName();
  Persion3.showName();

2.什么是原型链?

查找对象的属性(有2种,1是普通属性,2是方法)的时候先在自身找,如果自身没有沿着_ proto _找原型对象

如果原型对象上还没有,继续沿着_ proto _,直到找到object的原型对象

如果还没有找到返回undefined

原型链:沿着_ proto _查找的这条链就是原型链

3.new操作符都干了哪些事?

创建一个空对象
将this指向该对象
执行函数
将执行的结果返回

  function Persion(name,age){
         this.name = name;
         this.age = age;
}
  var Persion1 = new persion"kobe"43;
  console.log(Persion1);
//执行流程
  new操作符创建一个空对象{}this指向该对象this-->{}
  执行函数,将name,age属性添加进空对象中
  返回执行结果

构造函数的作用是什么?

不就是对一个空对象打磨加工嘛,这就好比一个模具,而new操作符所创建的空对象好比原材料!

4.变量提升和函数提升

js引擎在js代码正式执行之前会做一些预解析的工作
找关键字:var function
找到var后将var后边的变量提前声明,但是不赋值 var a
找到function以后定义对应的函数,也就是说函数在预解析的时候已经定义完毕
预解析:全局预解析 局部预解析
注意:全局预解析在定义函数的时候一定会定义函数,不关心函数是否被调用;局部预解析的时候如果内部函数没有被使用,不会提前定义

面试题:输出什么?
var a = 4function fn(){
console.log(a);
var a = 5};
fn();
//输出undefined
//首先在局部作用域里面找var a,找到并将变量a提升,并未赋值

5.执行上下文理解

1.执行上下文环境
js引擎在js代码正式执行之前会先创建一个执行环境(开发商批的地,工程队施工的环境)
分类:
全局执行上下文
局部(函数)执行上下文
eval执行上下文
2.执行上下文的变量对象
js引擎进入执行上下文环境之后先创建一个变量对象
该对象用于收集当前执行上下文环境中的:变量,函数,函数参数,this
确认this的指向
创建作用域链
3.执行上下文栈
作用:用于保存执行上下文(变量对象)
遵循规则:先进后出,后进先出
特点:执行上下文是动态创建的 -->针对函数,函数每调用一次就创建一次执行上下文,执行完就销毁

  var a = 10;
  var bar = function (x) {
      var b = 5;
      Foo(x + b);
  };
  var Foo = function (y) {
      var c = 5;
      console.log(a + c + y);
  };
  bar(10);

在这里插入图片描述

什么时候压栈?
答:一旦函数被调用,预解析就要压进来
什么时候出栈?
答:函数全部执行完毕
a,bar和Foo是怎么带进去栈里面的?
答:被所属环境里面的变量对象带进去的,a处在全局执行环境,被全局变量对象带进去,bar处于局部执行环境,被函数变量对象带进去,Foo也是如此

面试题:依次输出什么?
console.log('global begin: '+ i)
  var i = 1
  foo(1);
  function foo(i) {
    if (i == 4) {
      return;
    }
    console.log('foo() begin:' + i);
    foo(i + 1);
    console.log('foo() end:' + i);
  }
  console.log('global end: ' + i)
面试题1:a输出是什么?
console.log(typeof a);
var a;
function a(){
}//function
先预处理函数,后处理变量,如果有存在同名变量就会被忽略,不会再被定义
var a时,预处理a,但未赋值,读到function a时,定义这个函数,变量a之前有了,不会再被定义,但是会将内容function赋值给a!

面试题2:a输出是什么?
console.log(typeof a);
function a(){
}var a;
//function
读到function a时,定义这个函数,a对应function,接下来读到var a时,由于a已被预先声明并有内容function了,所以a不会再被js引擎定义了
面试题:c输出结果是什么?
var c = 1function c(c){
console.log(c);
var c = 3};
c(2);
//报错,c is not a function
首先有2个同名变量c,预解析c被定义为function,即 c = function(){},那么在代码正式执行时候,会跳过function c(c){}这段函数,而跳过后c的值为1,用c(2)一调用,直接报错

6.作用域和作用域链
1.作用域理解

作用域是抽象概念,它是虚拟的,而作用链确实真实存在的
用来决定代码执行的范围,变量所属的范围
作用域是代码定义的时候决定的

2.作用

隔离变量,不同作用域下同名变量不会有冲突
规定当前函数之后创建作用域链的上一个链条是什么样的

3.作用链理解

多个上下级关系的作用域形成的链式关系, 它的方向是从下向上的(从内到外)
作用链是一个数组 结构的数据,保存着当前作用域的变量对象及其上级作用域的变量对象
直到全局的变量对象 查找变量时就是沿着作用域链来查找的

3.作用域链如何产生

函数在定义的时候自动添加一个属性 ‘[[Scopes]]’, 该属性保存的是其上级作用域链
当函数执行的时候,进入执行上下文环境,将创建的变量对象添加到‘[[Scopes]]’数组的第一个位置,形成新的数组,该数组就是当前函数拥有的作用域链

4.查找一个变量的查找规则

先在当前作用域的变量对象中查找,如果有就使用
如果没有就会沿着作用域链的数组去上级作用域中的变量对象中查找 ,找到就返回对应的值
如果没有继续向上查找,直到找到最后一个变量对象(全局的变量对象),如果还没有就会报错

//创建全局作用域-->预解析工作-->创建全局的执行上下文-->执行上下文环境-->全局变量对象{a:undefined,this:window,fun:f(){}}
 var a = 123var b = "abc"//先定义-->创建局部作用域-->函数自身有一个[[scopes]]:上一级作用链(global)
 function fun(){
 //创建局部的执行上下文-->局部变量对象{a:undefined,fun2:function,this:window}-->创建作用链[局部变量对象,global]
       var a = 234var c = 345;
       console.log(a);
       console.log(b);
       //fun2函数已经被定义了-->创建局部作用域(fun2)-->函数自身有一个[[scopes]]:上一级作用域链[fun变量对象,global]
 function fun2(){
       //创建局部的执行上下文-->局部变量{this:window}-->创建作用链[fun2局部变量对象,fun的变量对象,global] 
       console.log(c);
       console.log(d);
}
 fun2();
};
 fun();

7.闭包
1.闭包形成的条件
函数嵌套
内部函数引用外部函数的局部变量对象
内部函数被使用(注意:函数变量提升的时候如果内部函数没有被使用,在预解析的过程中不会定义内部函数)

2.什么是闭包
闭包是一个存在内部函数的引用关系
该引用指向的是外部函数的局部变量对象(前提是内部函数使用了外部函数的局部变量)

3.闭包图解

 function fun(){
       var a = 123function fun2(){
      console.log(a);
}return fun2;
}var fun2 = fun();
 fun2();

在这里插入图片描述
4.闭包的运用
1.将一个函数作为另一个函数的返回值

//手写一个闭包
 function fun1(){
       var a = 2function fun2(){
       a++;
       console.log(a);
}return fun2;
}var fn = fun1();
 fn();

5.闭包的作用
延长外部函数变量对象的生命周期
使用闭包能够间接的从函数外部访问函数内部的私有变量

6.闭包的优缺点
优点:延长外部函数变量对象的生命周期
缺点:延长外部函数变量对象的生命周期(占内存,如果不及时清理容易造成内存溢出,泄露)

7.使用闭包的时候要注意:
及时清除闭包
让内部函数成为垃圾对象 -->内部函数身上没有指针,指向为null!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值