JS数据渲染机制以及堆栈内存

这是我建的qq群里面有很多资源,希望能和大家一起学习讨论共同进步!

这是群号:778734441

这是跟着周啸天老师一起学的笔记,后期我还会分享更好的资料给大家,希望大家多多支持,我会发布很多优秀的作品出来的!!!

一般都把 JS 放到BODY的末尾

  1. 为啥?
  2. 放到HEAD中可不可以 ? 如何放到HEAD中也可以实现出放到BODY末尾的效果?
  3. SCRIPT标签中有两个属性 : defer / async , 这两个属性是做什么的 ?
var a = 12 ; 
var b = a ;
b=13;
console.log(a);   ------>   12
 
var ary1   =  [ 12,23 ];
var ary2 =  ary1;
ary2.push ( 100 );
console.log ( ary1 );---->   [12,23,100];
复制代码

// =>

  1. 先声明一个变量a , 没有赋值 ( 默认值是undefined )
  2. 在当前作用域开辟一个文职存储12这个值
  3. 让变量a和12关联在一起 ( 定义 : 赋值 )

变量提升机制

当栈内存或者作用域形成 , JS自上而下执行之前 , 浏览器首先会把所有带 " VAR "/" function " 关键字的进行提前" 声明 " 或者 " 定义 " 这种预先处理机制称之为 " 变量提升 " => 声明( declare ) : var a / function sum ( 默认值undefined ) => 定义( defined ) : a = 12 ( 定义其实就是赋值 ) [ 变量提升阶段 ] => 带 " VAR " 的只声明未定义 => 带 " FUNCTION " 的声明和赋值都完成了

=> 变量提升只发生在当前作用域 ( 例如 : 开始加载页面的时候只对全局作用域下的进行提升 , 因为此时函数作存储的都是字符串而已 )在栈内存形成之后,JS代码执行之前

=> 在全局作用域下声明的函数或者变量是 " 全局变量 " , 同理 , 在私有作用域下声明的变量是 "私有变量" [ 带VAR/FUNCTION的才是声明 ]

=>浏览器很懒 , 做过的事情不会重复执行第二遍 , 也就是当代码执行遇到创建函数这部分代码后 , 直接跳过即可 ( 因为在提升阶段就已经完成函数的赋值操作了 )

console.log( a ) ; // => undefined var a = 12 ;

带VAR和不带的区别

//=> 在全局作用域下声明一个变量 , 也相当于给WINDOW全局对象设置了一个属性 , 变量的值就是属性值( 私有作用域中声明的私有变量和WINDOW没啥关系 ) console.log( a ) ; // => undefined console.log( window.a ) ; //=>undefined
console.log( 'a' in window ); // => TRUE 在变量提升阶段 , 在全局作用域中声明了一个变量 A , 此时就已经把 A 当作属性值赋值给 WINDOW 了 , 只不过此时还没有给A赋值 , 默认值为UNDEFINED in : 检测某个属性是否隶属于这个对象 var a = 12 ; //=> 全局变量修改 , WIN的属性值也跟着修改 console.log( a ) ; // => 全局变量 A 12 console.log( window . a ) ; //=>WINDOW的一个属性名A 12 a = 13 ; console.log( window.a ) ; //=>13 window.a = 14 ; console.log( a ) ; //=>14

//=>全局变量和WIN中的属性存在 " 映射机制 "
//=>不加VAR的本质是WIN的属性 console.log( a ) ; //=> Uncaught ReferenceError: a is not defined console.log( window.a ); //=>undefined console.log( 'a' in window ); //=>false a = 12 ; //=>window.a = 12 console.log( a ) ; //=>12 console.log( window.a ) ; //=>12

var a = 12, b= 12; //=>这样写B是带VAR的

var a = b =12; //=>这样写B是不带VAR的

console.log(a,b); var a = 12, b = 12; function fn(){ console.log(a,b); var a =b =13; console.log(a,b); } fn( ); console.log(a,b);

//=> 匿名函数之函数表达式

变量提升 :var fn; => 只对等号左边进行变量提升 sum ( ); 2 fn ( ) => Uncaught TypeError : fn is not a function var fn = function(){ console.log(1) }; //=>代码执行到此N处会把函数作赋值给FN fn( ); //=>普通的函数 function sum() { console.log(2) }

条件判断下的变量提升

在当前作用域下 , 不管条件是否成立都要进行变量提升 => 带VAR的还是只声明 => 带FUNCTION的在老版本浏览器渲染机制下 , 声明+定义都处理,但是为了迎合ES6中的块级作用域 , 新版浏览器对于函数(在条件判断中的函数) ,不管条件否成立,都只是先声明,没有定义 console.log(a); => undefined if( 1==2 ){ var a =12; } console.log(a); => undefined

条件判断下的变量提升 到底有多坑

if( 1===1 ){ console.log( fn ); //=> 函数本身 : 当条件成立 ,进入到判断体中它是一个块级作用域) 第一件事并不是代码执行 , 而是类似于变量提升一样,先把FN声明和定义了,也就是判断体中代码执行之前 , FN就已经赋值了 function fn ( ) { console.log('ok') } } console.log(fn); //=>函数本身

重名问题的处理

  1. 带VAR和FUNCTION关键字声明相同的名字 , 这种也算是重名了( 其实是一个FN , 只是存储值的类型不一样 ) var fn = 12;
    function fn ( ){

}

  1. 关于重名的处理 : 如果名字重复了 , 不会重新的声明 , 但是会重新的定义 ( 重新赋值 ) [ 不管是变量提升还是代码执行阶段皆如此]

LET创建的变量不存在变量提升

在ES6中基于LET/CONST等方式创建变量或者函数 , 不存在变量提升机制 =>切断了全局变量和WINDOW属性的映射机制

=> 在相同的作用域中 , 基于LET不能声明相同名字的变量 ( 不管用什么方式在当前作用域下声明了变量 , 再次使用LET创建会报错 ) 虽然没有变量提升机制 , 但是在当前作用域代码自上而下执行之前 , 浏览器会做一个重复性检测 ( 语法检测 ) : 自上而下查找当前作用域下所有变量 , 一旦发现有重复的 , 直接抛出异常 , 代码也不会执行了 ( 虽然没有把变量提前声明定义 , 但是浏览器已经记住了 , 当前作用域下有哪些变量 ) console.log ( a ) ; //=> Uncaught ReferenceError: a is not defined let a =12; console.log( window.a ) ; //=>undefined console.log( a ) ; //=>12

let a = 10 , b = 10; let fn = function ( ) { console.log( a,b ); //=>Uncaught ReferenceError: a is not defined let a = b = 20 ; let a = 20 ; b = 20; //=>把全局中的 b =20 console.log( a,b ); }; fn ( ); console.log( a ,b );

JS中的暂时性死区问题

var a = 12; if(true){ console.log(a); let a = 121; //=>Uncaught ReferenceError: Cannot access 'a' before initialization }

当函数执行 , 形成一个私有作用域 A ,A的上级作用域是谁 , 和它在哪执行的没有关系 , 和它在哪创建 ( 定义 ) 的有关系 , 在那创建的 ,他的上级作用域就是谁 var a = 12 ; function fn ( ) { //=> arguments : 实参集合 //=>arguments.callee : 函数本身FN //=>arguments.callee.caller : 当前函数在那执行的 ,CALLER就是谁( 记录的是它执行的宿主环境 ) , 在全局下执行CALLER的结果是NULL

console.log( a ) ; }

function sum ( ) { var a = 120; fn ( ); } sum ( );

JS中的堆栈内存

堆内存: 存储引用数据类型的值 ( 对象 : 键值对 函数 : 代码字符串 ) 栈内存 : 提供代码执行环境和存储基本类型的值

[ 堆内存释放 ] 让所有引用堆内存空间地址的变量赋值为null即可 ( 没有变量占用这个堆内存了, 浏览器会在空闲的时候把它释放掉 )

[栈内存释放] 一般情况下 , 当函数执行完成 , 所形成的私有作用域 ( 栈内存 ) 都会自动释放掉 ( 在栈内存中存储的值也会释放掉 ) , 但是也有特殊情况不销毁的情况 :

  1. 函数执行完成 , 当前形成的栈内存中 , 某些内容被栈内存以外的变量占用了, 此时栈内存不能释放 ( 一旦释放 , 外面找不到原有的内容了 )
  2. 全局栈内存只有在页面关闭的时候才会被释放掉 ... 如果当前栈内存没有被释放 , 那么之前在占内存存储的基本在也不会被释放 , 能够一直保存下来

【 闭包 】 => 函数执行形成一个私有的作用域 , 保护里面的私有变量不受外界的干扰 ,这种保护机制称之为 " 闭包 " => 市面上的开发者认为的闭包是 : 形成一个不销毁的私有作用域 ( 私有栈内存 ) 才是闭包

//=> 闭包 : 柯里化函数 function fn ( ) { return function ( ) { } } var f =fn();

//=> 闭包 : 惰性函数 var utils = (function(){ return { } })();

//=> 闭包项目实战应用 // ===>真实项目中为了保证JS的性能 ( 堆栈内存的性能优化 ) , 应该尽可能减少的使用 ( 不销毁的堆栈内存是消耗性能的 )

  1. 闭包具有保护作用 : 保护私有变量不受外界的干扰
  2. 闭包具有保存作用 : 形成不销毁的栈内存 , 把一些值保存下来 ,方便后面的调取使用

//=> 所有的事件绑定都是异步编程 ( 同步编程 : 一件事一件事的做,当前这件事没完成,下一个任务不能处理/ 异步编程 : 当前这件事没有彻底完成 , 不在等待,继续执行下面的任务 ) , 绑定事件后 , 不需要等待执行 , 继续执行下一个循环任务 , 所以当我们点击执行方法的时候 , 循环早已结束 ( 让全局的 I 等于循环最后的结果 )

THIS

  1. 给当前元素的某个事件绑定方法 , 当事件触发 , 方法执行的时候 , 方法中的THIS是当前操作的元素对象

=> 基于ES6中的LET来创建变量,是存在块级作用域的 ( 类似于私有作用域) 作用域 : ( 栈内存 )

  1. 全局作用域
  2. 私有作用域 ( 函数执行 )
  3. 块级作用域 ( 一般大括号包起来的都是块级作用域 , 前提是ES6语法规范 )

你可能感兴趣的:(JS数据渲染机制以及堆栈内存)