JS高级学习笔记2

函数高级

原型(prototype)

  1. 函数的prototype属性(图)
  • 每个函数都有一个prototype属性, 它默认指向一个Object空对象(即称为: 原型对象)
  • 原型对象中有一个属性constructor, 它指向函数对象
  1. 给原型对象添加属性(一般都是方法)
  • 作用: 函数的所有实例对象自动拥有原型中的属性(方法)

显式原型与隐式原型

  1. 每个函数function都有一个prototype,即显式原型(属性)
  2. 每个实例对象都有一个proto,可称为隐式原型(属性)
  3. 对象的隐式原型的值为其对应构造函数的显式原型的值
  4. 内存结构(图)
  5. 总结:
  • 函数的prototype属性: 在定义函数时自动添加的, 默认值是一个空Object对象
  • 对象的proto属性: 创建对象时自动添加的, 默认值为构造函数的prototype属性值
  • 程序员能直接操作显式原型, 但不能直接操作隐式原型(ES6之前)


    显式原型与隐式原型.png

原型链

  1. 原型链(图解)
    原型链的本质是隐式原型链,因为原型链的形成是真正是靠proto 而非prototype
    对象可以分为普通对象Object和函数对象Function()
  • 访问一个对象的属性时,
    • 先在自身属性中查找,找到返回
    • 如果没有, 再沿着proto这条链向上查找, 找到返回
    • 如果最终没找到, 返回undefined
  • 别名: 隐式原型链
  • 作用: 查找对象的属性(方法)
  1. 构造函数/原型/实体对象的关系(图解)
    三个版本的原型链图
原型链.png
原型链2.png
原型链3.png

测试题1
*/
function A () {

}
A.prototype.n = 1

var b = new A()

A.prototype = {
n: 2,
m: 3
}

var c = new A()
console.log(b.n, b.m, c.n, c.m) //1,undefined,2,3

/*
测试题2
*/
function F (){}
Object.prototype.a = function(){
console.log('a()')
}
Function.prototype.b = function(){
console.log('b()')
}

var f = new F()
f.a() // a()
f.b() //报错,f.b is not a function
F.a() //a()
F.b() //b()

原型链的属性问题

  1. 读取对象的属性值时: 会自动到原型链中查找
  2. 设置对象的属性值时: 不会查找原型链, 如果当前对象中没有此属性, 直接添加此属性并设置其值
  3. 方法一般定义在原型中, 属性一般通过构造函数定义在对象本身上

constructor属性

var p = new Person();
//方法才有prototype,普通对象无prototype
console.log(Person.prototype); // Object{} 
console.log(p.prototype); // undifined

//任何对象都是有构造函数constructor,由构造函数创建的对象也可以获得构造函数的引用
//此处只是打印下列对象的构造函数是什么。
console.log(p.constructor); //function Person(){}  
console.log(Person.constructor); //function Function(){} 
console.log({}.constructor); // function Object(){}
console.log(Object.constructor); // function Function() {}
console.log([].constructor);  //function Array(){} 

instanceof

  1. instanceof是如何判断的?
  • 表达式: A instanceof B
  • 如果B函数的显式原型对象在A对象的原型链上, 返回true, 否则返回false
  1. Function是通过new自己产生的实例,所以有Function__proto__ === Function.prototype

案例1
*/
function Foo() { }
var f1 = new Foo()
console.log(f1 instanceof Foo) // true
console.log(f1 instanceof Object) // true

/*
案例2
*/
console.log(Object instanceof Function) // true
console.log(Object instanceof Object) // true
console.log(Function instanceof Function) // true
console.log(Function instanceof Object) // true

function Foo() {}
console.log(Object instanceof Foo) // false

参考链接:最详尽的 JS 原型与原型链终极详解,没有「可能是」。(一) - 简书 (jianshu.com)

执行上下文和执行上下文栈

变量声明和函数提升

  1. 变量声明提升
  • 通过var定义(声明)的变量, 在定义语句之前就可以访问到
  • 值: undefined
  1. 函数声明提升
  • 通过function声明的函数, 在之前就可以直接调用
  • 值: 函数定义(对象)
    先执行变量提升, 再执行函数提升
    若使用var f = function(){}则不会产生函数提升,这种情况相当于是变量f的提前声明

执行上下文

  1. 代码分类(位置)
  • 全局代码
  • 函数(局部)代码
  1. 全局执行上下文
  • 在执行全局代码前将window确定为全局执行上下文
  • 对全局数据进行预处理
    • var定义的全局变量==>undefined, 添加为window的属性
    • function声明的全局函数==>赋值(fun), 添加为window的方法
    • this==>赋值(window)
  • 开始执行全局代码
  1. 函数执行上下文
  • 在调用函数, 准备执行函数体之前, 创建对应的函数执行上下文对象(虚拟的, 存在于栈中)
  • 对局部数据进行预处理
    • 形参变量==>赋值(实参)==>添加为执行上下文的属性
    • arguments==>赋值(实参列表), 添加为执行上下文的属性
    • var定义的局部变量==>undefined, 添加为执行上下文的属性
    • function声明的函数 ==>赋值(fun), 添加为执行上下文的方法
    • this==>赋值(调用函数的对象)
  • 开始执行函数体代码

执行上下文栈

  1. 在全局代码执行前, JS引擎就会创建一个栈来存储管理所有的执行上下文对象
  2. 在全局执行上下文(window)确定后, 将其添加到栈中(压栈)
  3. 在函数执行上下文创建后, 将其添加到栈中(压栈)
  4. 在当前函数执行完后,将栈顶的对象移除(出栈)
  5. 当所有的代码执行完后, 栈中只剩下window

练习
测试题1:
*/
function a() {}
var a
console.log(typeof a) // 'function'
(说明先执行变量提升, 再执行函数提升)

/*
测试题2:
/
if (!(b in window)) {
var b = 1
}
console.log(b) // undefined
(b是全局变量,会提前声明作为window的属性,由于if语句没有执行,因此b的值为undefined)
/

测试题3:
*/
var c = 1
function c(c) {
console.log(c)
var c = 3
}
//c(2) // 报错

作用域与作用域链

作用域

  1. 理解
  • 就是一块"地盘", 一个代码段所在的区域
  • 它是静态的(相对于上下文对象), 在编写代码时就确定了
  1. 分类
  • 全局作用域
  • 函数作用域
  • 没有块作用域(ES6有了)
  1. 作用
  • 隔离变量,不同作用域下同名变量不会有冲突

作用域与执行上下文

  1. 区别1
  • 全局作用域之外,每个函数都会创建自己的作用域,作用域在函数定义时就已经确定了。而不是在函数调用时
  • 全局执行上下文环境是在全局作用域确定之后, js代码马上执行之前创建
  • 函数执行上下文是在调用函数时, 函数体代码执行之前创建
  1. 区别2
  • 作用域是静态的, 只要函数定义好了就一直存在, 且不会再变化
  • 执行上下文是动态的, 调用函数时创建, 函数调用结束时就会自动释放
  1. 联系
  • 执行上下文(对象)是从属于所在的作用域
  • 全局上下文环境==>全局作用域
  • 函数上下文环境==>对应的函数使用域

作用域链

  1. 理解
  • 多个上下级关系的作用域形成的链, 它的方向是从下向上的(从内到外)
  • 查找变量时就是沿着作用域链来查找的
  1. 查找一个变量的查找规则
  • 在当前作用域下的执行上下文中查找对应的属性, 如果有直接返回, 否则进入上一级作用域
  • 在上一级作用域的执行上下文中查找对应的属性, 如果有直接返回, 否则继续进入上一级作用域查找
    *直到全局作用域, 如果还找不到就抛出找不到的异常

测试题

题1.

      var x = 10;
      function fn() {
        console.log(x);
      }
      function show(f) {
        var x = 20;
        f();
      }
      show(fn);    //10

题2

      var fn = function () {
        console.log(fn)
      }
      fn()    //输出fn函数
      var a = 2
      var obj = {
        a:1,      //   注意:对象的各属性间用逗号隔开,使用=会报错
        fn2:function () {
         //console.log(fn2)     //报错,全局作用域中不存在fn2变量
         console.log(this)     //   输出obj
         console.log(this.fn2)   //  输出fn2函数
         console.log(a)   //  2
         console.log(this.a)   //   1(若不显示指定,还是在全局作用域中寻找)
        }
      }
      obj.fn2()

参考链接:JS中对象的作用域?或者是对象的属性的一些疑问? - SegmentFault 思否

闭包

什么是闭包

  1. 如何产生闭包?
  • 当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时, 就产生了闭包
  1. 闭包到底是什么?
  • 使用chrome调试查看(新版chrome浏览器需要手动return 子函数才能看到)
  • 理解一: 闭包是嵌套的内部函数(绝大部分人)
  • 理解二: 包含被引用变量(函数)的对象(极少数人)
  • 注意: 闭包存在于嵌套的内部函数中
  1. 产生闭包的条件?
  • 函数嵌套
  • 内部函数引用了外部函数的数据(变量/函数)
  1. 常见使用闭包情况
  • 将函数作为另一个函数的返回值

  • 将函数作为实参传递给另一个函数调用

        // 1. 将函数作为另一个函数的返回值
        function fn1() {
          var a = 2
          function fn2() {
            a++
            console.log(a)
          }
          return fn2
        }
        var f = fn1()
        f() // 3
        f() // 4
      
        // 2. 将函数作为实参传递给另一个函数调用
        function showDelay(msg, time) {
          setTimeout(function () {
            alert(msg)
          }, time)
        }
        showDelay('atguigu', 2000)
    

闭包的作用

  1. 使用函数内部的变量在函数执行完后, 仍然存活在内存中(延长了局部变量的生命周期)
  2. 让函数外部可以操作(读写)到函数内部的数据(变量/函数)

问题:

  1. 函数执行完后, 函数内部声明的局部变量是否还存在? 一般是不存在, 存在于闭中的变量才可能存在
  2. 在函数外部能直接访问函数内部的局部变量吗? 不能, 但我们可以通过闭包让外部操作它

闭包的生命周期

  1. 产生: 在嵌套内部函数定义执行完时就产生了(不是在调用)

  2. 死亡: 在嵌套的内部函数成为垃圾对象时

       function fn1() {
         //此时闭包就已经产生了(函数提升, 内部函数对象已经创建了)
         var a = 2
         function fn2 () {
           a++
           console.log(a)
         }
         return fn2
       }
       var f = fn1()
       f() // 3
       f() // 4
       f = null //闭包死亡(包含闭包的函数对象成为垃圾对象)
    

闭包的应用

闭包的应用2 : 定义JS模块

  • 具有特定功能的js文件
  • 将所有的数据和功能都封装在一个函数内部(私有的)
  • 只向外暴露一个包信n个方法的对象或函数
  • 模块的使用者, 只需要通过模块暴露的对象调用方法来实现对应的功能

闭包的缺点

  1. 缺点
  • 函数执行完后, 函数内的局部变量没有释放, 占用内存时间会变长
  • 容易造成内存泄露
  1. 解决
  • 能不用闭包就不用
  • 及时释放 (设置为Null)

练习题

题1

      //代码片段一
      var name = "The Window";
      var object = {
        name : "My Object",
        getNameFunc : function(){
          return function(){
            return this.name;
          };
        }
      };
      alert(object.getNameFunc()());  //  the window
    
    
      //代码片段二
      var name2 = "The Window";
      var object2 = {
        name2 : "My Object",
        getNameFunc : function(){
          var that = this;
          return function(){
            return that.name2;
          };
        }
      };
      alert(object2.getNameFunc()()); //  my object

题2

    <script type="text/javascript">
        function fun(n,o){
            console.log(o)
            return {
                fun:function(m){
                    return fun(m,n)
                }
            }
        }
        var a = fun(0);a.fun(1);a.fun(2);a.fun(3);//undefined 0 0 0 
        var b = fun(0).fun(1).fun(2).fun(3);// undefined 0 1 2
        var c = fun(0).fun(1);c.fun(2);c.fun(3);// undefined 0 1 1
    </script>
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,869评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,716评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,223评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,047评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,089评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,839评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,516评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,410评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,920评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,052评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,179评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,868评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,522评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,070评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,186评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,487评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,162评论 2 356

推荐阅读更多精彩内容