今天是我参加字节跳动青训营的第四天
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() 【女娲】。
js 闭包
什么是闭包? js中闭包closure是指函数作用域内,因此看起来函数将变量“包裹”了起来 ,根据定义包含变量的函数就是函数闭包。
//根据定义包含变量的函数就是闭包
fuction foo(){

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的。
闭包的生命周期
- 产生:在嵌套内部函数定义执行完时就产生(不是在调用)
- 死亡:在嵌套的内部函数变为垃圾对象时.
闭包的缺点
-
缺点
- 函数执行完后,函数内的局部变量没有释放,占用内存时间会变长
- 容易造成内存泄露
-
解决
-
能不用闭包就不用
-
及时释放
-
关于闭包的内存泄漏问题
-
内存溢出
- 一种程序运行出现的错误
- 当程序运行需要得到内存超过了剩余的内存时,就会抛出内存溢出的错误
-
内存泄漏
-
占用的内存没有及时释放
-
内存泄漏积累多了就容易导致内存溢出
-
常见的内存泄漏
- 意外的全局变量
- 没有及时清理的计时器或回调函数
- 闭包