js | 青训营笔记

37 阅读5分钟

今天是我参加字节跳动青训营的第四天

js 的prototype

prototype是什么

JavaScript在设计之初,是作为一种网页脚本语言,没有设计得很复杂,这种语言只要能够完成一些简单操作就够了。Javascript里面所有的数据类型都是对象(object)。在ES6之前,js中是没有Class的概念的(ES6中的类也是语法糖,本质还是基于原型),为了实现实例对象的属性和方法共享,就给function设计了一个prototype的概念。
prototype是一个对象。他是如何工作的呢?
简单地说,JavaScript 是基于原型的语言。当我们调用一个对象的属性时,如果对象没有该属性,JavaScript 解释器就会从对象的原型对象上去找该属性,如果原型上也没有该属性,那就去找原型的原型,直到最后返回null为止,null没有原型。这种属性查找的方式被称为原型链(prototype chain)。

// 让我们从一个函数里创建一个对象 o,它自身拥有属性 a 和 b 的:
let f = function () {
   this.a = 1;
   this.b = 2;
}
/* 这么写也一样
function f() {
  this.a = 1;
  this.b = 2;
}
*/
let o = new f(); // {a: 1, b: 2}

// 在 f 函数的原型上定义属性
f.prototype.b = 3;
f.prototype.c = 4;

// 不要在 f 函数的原型上直接定义 f.prototype = {b:3,c:4};这样会直接打破原型链
// o.[[Prototype]] 有属性 b 和 c
//  (其实就是 o.__proto__ 或者 o.constructor.prototype)
// o.[[Prototype]].[[Prototype]] 是 Object.prototype.
// 最后 o.[[Prototype]].[[Prototype]].[[Prototype]] 是 null
// 这就是原型链的末尾,即 null,
// 根据定义,null 就是没有 [[Prototype]]。

// 综上,整个原型链如下:

// {a:1, b:2} ---> {b:3, c:4} ---> Object.prototype---> null

console.log(o.a); // 1
// a 是 o 的自身属性吗?是的,该属性的值为 1

console.log(o.b); // 2
// b 是 o 的自身属性吗?是的,该属性的值为 2
// 原型上也有一个'b'属性,但是它不会被访问到。
// 这种情况被称为"属性遮蔽 (property shadowing)"

console.log(o.c); // 4
// c 是 o 的自身属性吗?不是,那看看它的原型上有没有
// c 是 o.[[Prototype]] 的属性吗?是的,该属性的值为 4

console.log(o.d); // undefined
// d 是 o 的自身属性吗?不是,那看看它的原型上有没有
// d 是 o.[[Prototype]] 的属性吗?不是,那看看它的原型上有没有
// o.[[Prototype]].[[Prototype]] 为 null,停止搜索
// 找不到 d 属性,返回 undefined

代码来源链接:repl.it/@khaled_hos…

为了方便举例,我们在这模拟一个场景,父类比作师父,子类比作徒弟。师父收徒弟, 徒弟还可以收徒弟。徒弟可以得到师父传授的武功,然后徒弟再传给自己的徒弟。 师父想要传授给徒弟们的武功就放到“prototype”这个琅琊福地中。徒弟徒孙们就去这里学习武功
prototype属性可以看成是一块特殊的存储空间,存储了供“徒弟”、“徒孙”们使用的方法和属性。
__proto__属性相当于通往prototype(“琅琊福地”)唯一的路(指针) 让“徒弟”、“徒孙” 们找到自己“师父”、“师父的师父” 提供给自己的方法和属性
constructor属性是让“徒弟”、“徒孙” 们知道是谁创造了自己,这里可不是“师父”啊 而是自己的父母,父母创造了自己,父母又是由上一辈人创造的,……追溯到头就是Function() 【女娲】。

image.png

js 闭包

什么是闭包? js中闭包closure是指函数作用域内,因此看起来函数将变量“包裹”了起来 ,根据定义包含变量的函数就是函数闭包。

//根据定义包含变量的函数就是闭包
fuction foo(){

![image.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f3a2824129c542d78e33e0d956e837f6~tplv-k3u1fbpfcp-watermark.image?)
    var a = 0;
 
 }
 cosole.log(a)

《JavaScript高级程序设计》对比报的定义 闭包是指有权访问另一个函数作用域中的变量

function foo(){
    var a =2;
    fucntion bar() {
        console.log(a);
    }
    bar()
}
foo();

函数对象可以通过作用域链相关联起来,函数体内部变量可以保存在函数作用域内,这就是闭包。 严格要求来说闭包必须满足三个条件:

  • 访问在作用域内
  • 函数内嵌套
  • 在所在作用域外被调用

闭包有什么作用

既然说到闭包我们要想js 闭包有什么用

 function foo() {
     var counter= 0;
       return counter += 1;
      }
      add();
      add();
      add(); //想要输出3,但输出都是1

因为每次调用add() 函数counter的值都要被初始化为0,结果是不行的 这时候就引出闭包了

var add = (function() {
    var counter = 0;
    return function{return counter += 1;}
 })();
 add();
 add();
 add();//计数为3
  

分解如下

function outerFunction (){
    var counter = 0;
    function innerFunction (){
    return counter += 1;
    }
    return innerFunction;
 }
 var add = outerFunction();
 add();
 add();
 add();//计数器为3

一个闭包由两部分组成,函数和创建该函数的环境。环境是由环境中的局部变量组成的。对于闭包add来说,它是由函数inner Function和变量counter组成,所以这时候add是可以访问变量counter的。

闭包的生命周期

  1. 产生:在嵌套内部函数定义执行完时就产生(不是在调用)
  2. 死亡:在嵌套的内部函数变为垃圾对象时.

闭包的缺点

  1. 缺点

    • 函数执行完后,函数内的局部变量没有释放,占用内存时间会变长
    • 容易造成内存泄露
  2. 解决

    • 能不用闭包就不用

    • 及时释放

关于闭包的内存泄漏问题

  1. 内存溢出

    • 一种程序运行出现的错误
    • 当程序运行需要得到内存超过了剩余的内存时,就会抛出内存溢出的错误
  2. 内存泄漏

  • 占用的内存没有及时释放

  • 内存泄漏积累多了就容易导致内存溢出

  • 常见的内存泄漏

    • 意外的全局变量
    • 没有及时清理的计时器或回调函数
    • 闭包