1 JavaScript有哪些数据类型
1.1 基本数据类型
number、string、boolean、null、undefined
1.2 引用数据类型
object:{}、[ ]、/$/、Date
function
2 全局作用域(global/window)
当浏览器加载html页面的时候,首先会提供一个全局jsdiam执行的环境
var num = 12;
var obj = {name:"wjw",age:7);
function fn(){
console.log("勿忘初心方得始终")
}
console.log(fn); // 把整个函数的定义部分(函数本身在控制台输出)
console.log(fn()); //把当前函数执行的返回结果(return后面写的是啥,返回就是啥,如果没有return,默认返回undefined)

3 预解释(变量提'前'声'明')
在当前的作用域中,js代码执行之前,浏览器首先会默认的把所有带var和function的进行提前的声明或者定义
3.1 声明(declare)
var num ;
告诉浏览器在全局作用域中有一个num的变量了
如果一个变量只是声明了,但是没有进行变量的定义,那就是undefined
3.2 定义(define)
num = 12 //给我们的变量进行赋值
3.3 var
在预解释的时候只是提前的声明
3.4 function
在预解释的时候,提前的声明+定义都完成了
对于带var和function关键的预解释的操作还是不一样
fn(100,200);//可以在上面执行,因为预解释的时候,声明+定义已经完成了
function fn(num1,num2){
var total = num1 + num2;
console.log(total);
}
3.5 预解释只发生在当前的作用域之下
例如:开始只对window下得进行预解释,只有函数执行的时候才会对函数的进行预解释

4 js中内存的分类
4.1 栈内存
用来提供一个js执行的环境 -> 作用域(全局的作用域,私有)
4.2 堆内存
用来存储引用数据类型的值 -> 对象存储属性名和属性值,函数存储的是代码字符串
5 如何区分私有变量和全局变量?
- 在全局作用域下声明(预解释的时候)的变量就是全局变量
- 在私有作用域中声明变量和函数的形参都是私有的变量
在私有作用域中,我们代码执行的时候遇到一个变量,首先我们需要确定当前它,是否是私有变量,如果是私有的变量,那么和外面的任何东西都没有关系;如果不是私有的,则往当前作用域上级作用域进行查找,如果上级作用域也没有则继续查找,一直找到window为止..
6 当函数执行的时候
(直接目的:让函数体重的代码执行),首先会形成一个新的私有的作用域
1.如果有形参,先给形参赋值
2.进行私有作用域中的预解释
3.私有作用域中的代码从上到下执行
console.log(total); // undefined
var total = 0;
function fn(num1,num2){
console.log(total); // total 不是私有的,找全局下的total,也就是在这里出现所有的total其实应该都是
var total = num1 + num2;// 全局的total=300
console.log(total);// 300
}
fn(100,200);
console.log(total);// 300
7 闭包
函数形成一个新的私有的作用域保护了里面的私有变量不受外界的干扰(修改不了私有的,私有的也修改不了外面的
在全局作用域中,带var和不带var的关系?
区别:带var的可以进行预解释,所以在赋值的前面执行不会报错;不带var的是不能进行预解释的,在前面执行会报错
console.log(num);//undefined
var num = 12;
console.log(num2); // Uncaught ReferenceError:num2 is no defined
num2 = 12;
关系:num = 12 ->相当于给window增加了一个叫做num的属性名,属性值12
var num =12 ->首先它相当于给全局作用域增加了一个全局变量num,但是不仅如此,它也相当于给window增加了一个属性名怒,属性值12
var num = 12;
console.log(num); // 12
num2 = 12;
console.log(num2);// 12 window.num2
私有作用域中出现的一个变量不是私有的,则往上级作用域进行查找,上级没有则继续向上级查询,一直找到window为止
funtciont fn(){
console.log(total1); // Uncaught ReferenceError:num2 is no defined
total = 100;
}
fn();
console.log(total1)
js中如果不进行任何特殊处理的情况下,上面的代码报错,下面的代码都不在执行了
8 预解释的解析
in "num" in window 判断num是否为window这个对象的一个属性,是的话返回true,不是返回flase
console.log("name" in obj)
8.1 预解释的时候不管你的条件是否成立,都要把var的进行提前的声明
window的预解释:var num - > window num
if(!("num") in window){ // “num” in window ->true
var num = 12
}
console.log(num) -> undefind
8.2 预解释的时候只预解释“=”左边的,右边的是值,不参与预解释
匿名函数值函数表达式:把函数定义跌部分当做一个值赋值给我们的变量(元素的某一个事件)
window下的预解释 var fn;
fn(); // undefined
var fn = function(){
conslole.log("ok")
}
fun(); // ok
function fn(){
console.log("ok")
}
fn(); // ok
8.3 执行函数定义的那个function在全局作用下不进行预解释,当代名执行到这个位置的时候,和执行一起完成
自执行函数: 定义和执行的一起完成了
(function (params) {})(100);
~function (params) {}(100);
+function (params) {}(100);
-function (params) {}(100);
!function (params) {}(100);
8.4 函数体中return 下面的代码虽然不在执行,但是需要进行预解释,跟着都返回给我们的值,所以不进行预解释
function fn(){
// 预解释:var num;
console.log(num); // ->undefined
return function(){}; // -> 不预解释
var num = 100
}
fun();
8.5 在js中如果变量的名字和函数的名字重复
预解释:var fn ; window.fn; fn-xxxfff000 window.fn-xxxfff00
var fn = 13;
function fn() {
console.log("ok");
}
window 预解释 声明+定义 fn = xxxfff11 声明 var fn; (不需要重新声明) 声明(不重复进行)+定义 fn = xxxfff222 fn = xxxfff22
fn(); // ->2
function fn() {console.log(1)}; //-> 2
fn(); // 2
var fn = 10;
fn (); // error 终止了
function fn() {console.log(2)};
fn();
9 如何查找上级作用域
看当前函数是哪个作用域下定义的,那么它的上级作用域就是谁
9.1 代码
let num =12;
function fn() {
var num = 120;
return function () {
console.log('====================================');
console.log(num);
console.log('====================================');
}
}
var f = fn();
f(); // 这样就执行了第1次
~function() {
var num = 1200;
f(); // 这样就执行了第2次
}()
9.2 运行结果
var f = fn()就是把函数fn的运行返回结果赋值给f,fn函数返回的是一个函数,该返回函数的上级作用域为函数fn
的私有作用域,所以运行f函数console.log(num),num不是f私有作用域的私有变量,需要在上级作用域找,fn的私有作用域含有变量
num,所以都输出的num为120。

10 自执行匿名函数
- 常见格式:(function(){ /* code */ })();
- 解释:包围函数(function(){ /* code */ })的第一对括号向脚本返回未命名的函数,随后一对空括号立即执行返回的未命名函数,括号内为匿名函数的参数。
- 作用:可以用它创建命名空间,只要把自己的所有代码都写在这个特殊的函数包装内,那么外部就不能访问,除非你允许(变量前加上window,这样该函数后变量就成为全局)。各JavaScript库的代码也基本上是这种组织形式。
总结一下,执行函数的作用主要为匿名和自动执行,代码在被解释时就已经在运行了。
(function(){ /* code */ }());
!function(){ /* code */ }();
~function(){ /* code */ }();
-function(){ /* code */ }();
+function(){ /* code */ }();
~function(a) {
console.log('====================================');
console.log(a);
console.log('====================================');
}(10)
11 关于内存释放和作用域销毁的研究
11.1 堆内存
对象数据类型或者函数数据类型在定义的时候首先都会开辟一个堆内存,堆内存有一个引用的地址,如果外面有变量等知道这个地址,外面就说这个内存被占用,就不能销毁。
var obj1 = {name:"张三"};
var obj2 = obj1;
我们想要让堆内存释放/销毁,只需要吧所有引用它的变量值赋值为null即可,如果当前的堆内存没有任何东西呗占用了,那么浏览器会在空间的时候把它销毁...
obj1 = null;
obj2 = null;
11.2 栈内存
11.2.1 全局作用域
11.2.2 私有的作用域
一般情况下,函数执行会形成一个新的私有的作用域,当私有作用域中的代码执行完成后,我们当前作用域都会主动的进行释放和销毁
但是还是存在特殊的情况的:
当前私有作用域中的部分内存被作用域以外的东西占用了,那么当前的这个作用域就不能销毁了
a.函数执行返回了一个引用数据类型的值,并且在函数的外面被一个其他的东西给接收了,这种情况下形成的私有作用域都不会销毁
function fn(){
var num = 100;
return function(){
num++;
console.log(num);
}
}
var f = fn(); // fn执行形成这个私有的作用域就不能再销毁了
b.在一个私有的作用域中给dom元素的事件绑定方法,一般情况下我们的私有作用域都不会销毁
var oDLv = document.getElementById('div1');
~function(){
oDLv.onclick = function(){
}
}() // 当前自执行函数形成一个私有的作用域也不销毁
c.下述情况属于不立即销毁->fn返回的函数没有被其它的东西占有,但是还需要执行一次呢,所以暂时不销毁,当返回的值执行完成后,浏览器会在空闲的时候把它销毁
function fn(){
var num = 100;
return function(){
}
}
12 作用域练习题
都是自身累加1,在和其他的值进行运算的时候是有区别的
- i++: 先拿i的值进行运算,运算完成本身+1
- ++i: 先本身累加1,然后拿累加完成的结果去运算
var i = 5;
console.log(1+i++);//6 i=6
console.log(1+(++i));//6 i=7
console.log(2+(i++)+(++i)+(++i)+(i++));//38
console.log(i);// 9
function fn(){
var i = 10;
return function (n){
console.log(n+(++i));
}
}
var f = fn();
f(10);// 21
f(20);// 32
fn()(10);// 21
fn()(20);// 31
function fn(){
var i = 10;
return function (n){
console.log(n+(++i));
}
}
var f = fn(13);
f(12);// 25
f(24);// 28
fn(15)(12);// 27
fn(16)(13);// 29
13 this关键字
我们在js中主要研究的都是函数中的this
js中的this代表的是当前行为执行的主体,js中的context代表的是当前行为的环境(区域)
例如:吴佳玮在沙县吃蛋炒饼,this->吴佳玮 context->沙县小吃
function 吃饭(){
this->吴佳玮
}
吴佳玮.吃饭();
~function(){
吴佳玮.吃饭();
}();
this是谁和函数在哪定义的和在哪执行的都没有任何关系:如何的区分 this呢
- 函数执行,首先看函数名前是否有".",有的话"."前面是谁this是谁;没有的话this是window
function fn(){
console.log(this);
}
var obj ={
fn:fn
};
fn(); // this->window
obj.fn();// this->obj
function sum(){
fn();
}
sum();
- 自执行函数中的this永远是window
- 给元素的某一个时间绑定方法,当时间触发的时候,执行对应的方法,方法中的this是当前的元素
<div id="div1">有本事点我啊~~</div>
...
<script>
document.getElementById("div1").onclick = fn;//fn中的this是#div1
document.getElementById("div1").onclick = fucntion(){
// this->#div1
fn(); // this->window
}
</script>
14 综合练习题
var num = 20;
var obj = {
num:30,
fn:(function(num){
this.num *=3;
num+=15;
var num = 45;
return function(){
this.num *=4;
num+=20;
console.log(num);
}
})(num) // 全局变量num的值20赋值给自执行函数的形参,而不是obj下的30,如果想是obj下的30,我们需要写obj.num
}
var fn = obj.fn;
fn();
obj.fn();

var oBth = document.getElementById("btn");
var spanNum = document.getElementById("spanNum")
// 利用全局作用域不销毁原理,把需要的数字定义为全局的变量
var count = 0;
oBtn.onclick = fucntion(){
count++;
spanNum.innerHTML = cont;
}
// 弊端:在项目中为防止全局变量之间冲突,我们一般是禁止或者减少使用全局变量的使用
// 自己形成一个不销毁的私有作用域来保存我们需要累加的数字
2、 利用innerHTML的方式处理,每一次点击时候都先到页面中获取最新的值,然后累加,最后把累加的结果重新放回去
var oBth = document.getElementById("btn");
var spanNum = document.getElementById("spanNum")
//
oBtn.onclick = fucntion(){
spanNum.innerHTML++;
}
// 弊端:每一次吧页面中的内容先转换为字符串然后再累加,累加玩之后重新添加回去,
// 当重新的添加的时候浏览器从新的渲染一下
3、利用自定义属性存储(推荐)
oBth.count = 0;
oBth.onclick = function(){
// spanNum.innerHTML获取页面中的内容返回的是一个字符串
spanNum.innerHTML = ++this.count;
}