# JavaScript-jichu **Repository Path**: iptu/JavaScript-jichu ## Basic Information - **Project Name**: JavaScript-jichu - **Description**: JavaScript基础、高级 - **Primary Language**: JavaScript - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 1 - **Created**: 2020-04-16 - **Last Updated**: 2021-07-26 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README #### 12.课件2&变量提升机制 #### 变量提升: ​ 当栈内存(作用域)形成,js代码自上而下执行之前,浏览器首先会把所有带var / function 关键词的进行提前 ‘声明’ 或者定义 - 形参赋值 - 变量提升 - 代码执行 ``` /* * 1.先声明一个变量a,没有赋值(默认值是undefined) * 2.在当前作用域中开辟一个位置存储12这个值 * 3.让变量a和12关联在一起(定义:赋值) - => 声明 : var a - => 定义 : a = 12 */ ``` ``` * 变量提升: * =>当栈内存(作用域)形成,JS代码自上而下执行之前,浏览器首先会把所有带 “VAR”/“FUNCTION” 关键词的进行提前 “声明” 或者 “定义” ,这种预先处理机制称之为 “变量提升” * * =>声明(declare):var a (默认值undefined) * =>定义(defined):a=12 (定义其实就是赋值操作) * * [变量提升阶段] * =>带“VAR”的只声明未定义 * =>带“FUNCTION”的声明和赋值都完成了 * * =>变量提升只发生在当前作用域(例如:开始加载页面的时候只对全局作用域下的进行提升,因为此时函数中存储的都是字符串而已) * =>在全局作用域下声明的函数或者变量是“全局变量”,同理,在私有作用域下声明的变量是“私有变量” [带VAR/FUNCTION的才是声明] * * =>浏览器很懒,做过的事情不会重复执行第二遍,也就是,当代码执行遇到创建函数这部分代码后,直接的跳过即可(因为在提升阶段就已经完成函数的赋值操作了) ``` #### 加var 和不加var 区别 - 加var 是一个变量、不加var 是win的一个属性 ```javascript var a = 12, b = 13;//=>这样写B是带VAR的 var a = b = 12;//=>这样写B是不带VAR的 ``` ```javascript function fn() { /* 变量提升:无 */ // console.log(b);//=>Uncaught ReferenceError: b is not defined b = 13; console.log('b' in window); /* =>true 在作用域链查找的过程中,如果找到WIN也没有这个变量,相当于给WIN设置了一个属性B (window.b=13) */ console.log(b);//=>13 } ``` 1-只对等号左边进行变量提升 ```javascript /* 变量提升: * var fn; =>只对等号左边进行变量提升 * sum = AAAFFF111; */ sum(); fn();//=>Uncaught TypeError: fn is not a function fn此时是undefined,调用的时候不是一个函数,所以报错 //=>匿名函数之函数表达式 var fn = function () { console.log(1); };//=>代码执行到此处会把函数值赋值给FN fn(); //=>普通的函数 function sum() { console.log(2); } ``` `caller` > 当前函数在哪里执行,caller及时谁,在全局执行caller的结果是null ```javascript function fn(){ console.log(arguments.callee.caller) } function sum() { var a = 20; fn(); } sum() //* sum() { var a = 20; fn(); } */ ``` #### JS中的内存分为堆内存和栈内存: - 堆内存:储存引用数据类型值(对象:键值对, 函数:代码字符串) - 栈内存:提供s执行的环境和储存基本类型值 ```javascript 堆内存释放: 让所有引用堆内存空间的地址的变量赋值内null 即可(没有变量占用这个堆内存了 ,浏览器会在空闲的时候把它释放掉) 栈内存释放: 一般情况下,当函数执行完成,所形成的私有作用域(栈内存) 都会自动释放掉(在栈内存中储存的值也都会释放掉) ,但是也有特殊不销毁的情况: 1.函数执行完成,当前形成的栈内存中,某些内容被栈内存以外的变量占用了,此时栈内存不能释放(一旦释放外面找不到原有的内容了) 2.全局栈内存只有在页面关闭的时候才会被释放掉 如果当前栈内存没有被释放,那么之前在栈内存中储存的的基本只也不会被释放,能够一直保存下来 ``` ```javascript [闭包] => 函数执行形成一个私有的作用域,保护里面的私有变量不受外界的干扰,这种保护机制称之为‘闭包’ => 市面上的开发者认为的闭包是:形成一个不销毁的私有作用域(私有栈内存) 才是闭包 function fn(){ rturn function fn(){} } var ut = (function(){ return {} })() ``` 同步异步: 同步:一件事一件事的做 异步:当前这个事情没有彻底完成,不在等待,继续执行下面的任务 (所有事件绑定是异步任务) --- #### 面向对象编程 OOP ```javascript /* * 单例设计模式(singleton pattern) * 1.表现形式 * var obj = { * xxx:xxx, * ... * }; * 在单例设计模型中,OBJ不仅仅是对象名,它被称为“命名空间[NameSpace]”,把描述事务的属性存放到命名空间中,多个命名空间是独立分开的,互不冲突 * * 2.作用 * =>把描述同一件事务的属性和特征进行“分组、归类”(存储在同一个堆内存空间中),因此避免了全局变量之间的冲突和污染 * var pattern1={name:'xxx'} * var pattern2={name:'xxx'} * * 3.单例设计模式命名的由来 * =>每一个命名空间都是JS中Object这个内置基类的实例,而实例之间是相互独立互不干扰的,所以我们称它为“单例:单独的实例” */ /* * 高级单例模式 * 1.在给命名空间赋值的时候,不是直接赋值一个对象,而是先执行匿名函数,形成一个私有作用域AA(不销毁的栈内存), * 在AA中创建一个堆内存,把堆内存地址赋值给命名空间 * * 2.这种模式的好处:我们完全可以在AA中创造很多内容(变量OR函数),哪些需要供外面调取使用的, * 我们暴露到返回的对象中(模块化实现的一种思想) */ /*var nameSpace = (function () { var n = 12; function fn() { //... } function sum() { } return { fn: fn, sum: sum } })();*/ ``` #### this指向 ```javascript /* * THIS * 1.给当前元素的某个事件绑定方法, 当事件触发方法执行的时候,方法中的THIS是当前操作的元素对象 * oBox.onclick=function(){ * //=>this:oBox * } * * 2.普通函数执行,函数中的THIS取决于执行的主体,谁执行的,THIS就是谁(执行主体:方法执行, * 看方法名前面是否有“点”,有的话,点前面是谁this就是谁,没有this是window)*/ ``` #### 函数创建 ```javascript 基于构造函数创建自定义类 1.在普通函数执行的基础上 'new XXX()',这样就不是普通函数执行了,而是构造函数执行,当前的函数名称之为'类名',接收的返回结果是当前类的一个实例! 2.自己创建的类名,最好第一个单词首字母大写 3.这种构造函数设计模式执行,主要用于组件、类库、插件、框架等的封装,平时编写业务逻辑一般不这样处理 function fn(){ } fn();//函数执行 var f = new Fn();//构造函数,Fn是类,f是类的一个实例 var obj1 ={}; //obj1是Object 的一个实例 单例模式 --------------------------------------------- 有new 就是构造函数,没有new就是普通函数 --------------------------------------------- JS创建值有两种方式 1.字面量表达式 2.构造函数模式 var obj = {}; //=> 字面量方式 var obj = new Object(); // => 构造函数模式 不管是哪种方式创造出来的都是Object类的实例,而实例之间是独立分开的,所以 var xxx = {} 这种模式就是JS中的单例模式 --------------------------------------------- 基于数据类型 基于两种不同的模式创建出来的值是不一样的 基于字面量方式创建出来的值是基本类型值 都是Number的实例,都可以用数字类型提供的属性和方法; var num1 = 12;//基本类型值 基于构造函数创建出来的值是引用类型 var num2 = new Number(12); //引用类型 console.log(typeof num1) // "number" console.log(typeof num2) // "Object" ``` #### 构造函数执行的机制 ```javascript 普通函数执行: function fn(){} fn(); 普通函数执行 1.形成一个私有的作用域 2.形参赋值 3.变量提升 4.代码执行 5.栈内存释放问题 构造函数执行: function Fn(name,age){ var n = 10; this.name=name; this.age = age + n ; return; //这样return 是结束代码的执行的作用,并不会覆盖返回的实例 console.log(123)// } var f = Fn('张三',20) 构造函数执行,不写return,浏览器会默认返回创建的实例,但是如果我们自己写return? 1.return是一个基本值,返回的结果依然是类的实例,没有受到影响。 2.如果返回的是一个引用值,则会把默认的实例覆盖,此时接收到的结果就不在是当前类的实例了。 =》构造函数执行的时候尽量减少return的使用,防止覆盖实例 ``` #### 原型链和原型链的查找机制 ```javascript 1.所有函数数据类型都天生自带一个属性:prototype(原型),这个属性的值是一个对象,浏览器会默认给它开辟一个堆内存 2.在浏览器给prototype 开辟的堆内存中有一个天生自带的属性:contructor,这个属性储存的值是当前函数本身 3.每一个对象都有一个 __proto__的属性,这个属性指向当前实例所属类的prototype(如果不能确定他是谁的实例,都是Object 的实例) ```