数据类型
基本数据类型
- number
- string
- boolean
- null
- undefined
- symbol
引用数据类型
对象
- {} 普通对象
- []数组
- /^$/ 正则
- Math
函数
- function普通函数
- 类
JS 代码运行在浏览器中,是因为浏览器提供了一个供JS代码执行的环境 ->
全局作用域(window/global)
var a = 12,
var b = a;
b = 13;
cobsole.log(a); // 12
var obj = {name: "lulu"};
var p = obj;
obj.name = "123";
console.log(obj.name); // 123
var m = {name:"javascript"};
var n = m;
n = {name:"前端开发"}; // n在这一步又指向别的空间,m和n就没关系了
console.log(m.name); // javascript
function fn(){
var ary = Array.prototype.slice.call(arguments) // 把类数组转化成数组
return eval(ary.join('+'));
}
值类型
按值操作 如var a = 12; 直接把12这个值赋给变量a(让两者建立了链接关系)
对象数据类型
对象类型:
1)浏览器为其开辟一个新的内存空间(不像值类型只有一个值,可以有很多的值),浏览器给空间分配一个16进制的地址,
2)按照一定顺序,分别把对象键值对存储到内存空间中
3)把这个地址赋值给引用类型变量,是两者关联,引用类型就可以通过该地址找到内存空间,对空间里面存储的值进行操作
函数的操作
创建函数(也是对象)
- 先开一个新的内存空间,为其分配一个16进制的地址(跟对象中的第一步别无二致)
- 把函数体中编写的js代码当作
字符串
存储到开辟的空间中(函数只创建不执行没啥意义)- 把分配的地址赋值给声明的函数名(function fn和var fn操作原理相同,都是在当前作用域中声明了一个名字,重复)
跟对象的区别:对象的内存中存的是有意义的字符串,而函数的内存中存的是无意义的字符串执行函数
执行函数其中的代码
- 函数执行的时候浏览器会形成一个新的私有作用域(只能执行函数体中的代码)供函数体中的代码执行
- 执行代码之前,先把创建函数时存储的字符串变为真正的js表达式,按照从上到下的顺序在私有作用域中执行
一个函数可以被执行很多次,每次执行相互之间互不干扰
形成的二私有作用域把函数体中的私有变量都保护起来了,在私有作用域中操作私有变量和外界没有关系,外界也无法直接操作私有变量,函数执行的这种保护机制叫做闭包
JS中的堆栈内存
栈内存
即作用域(全局作用域/私有作用域)
[作用]
- 为js代码提供执行的环境
- 基本数据类型是直接存放在栈内存中的
堆内存
存储引用数据类型值的,相当于一个存储的仓库
对象存储的是键值对
函数存储的是代码字符串
项目中内存越少越好,我们需要把一些没用的内存处理到
[堆内存]
var obj = {}; 当前对象对应的堆内存被变量obj占用,无法销毁
obj = null; 空对象指针,不指向任何堆内存,谷歌浏览器会在空闲时间把没有被占用的堆内存自动释放(销毁/回收)
[栈内存]
一般情况下函数执行形成栈内存,若函数执行完成,浏览器会把形成的栈内存自动释放;有时候执行完成栈内存不能被释放
全局作用域在加载页面时执行,关闭页面时销毁
变量提升
在
当前作用域
中,js代码自上而下执行,浏览器首先会把var/function
关键字进行提前的声明或者定义
声明(declare): var num; 告诉当前作用域有这个名字了
定义(defined): num = 21; 把声明的名字赋一个值
var关键字的只是声明,function声明加定义一起完成
console.log(num);
console.log(fn);
var num = 13;
function fn(){
console.log(a);
var a = 10;
console.log(a);
}
fn();
console.log(num);
变量声明时带var和不带的区别
带var在当前作用域中创建了一个变量。(若当前是全局作用域,也会给全局作用域下添加这个属性)
在全局作用域中不带var,仅是给全局对象设置了一个新的属性名,省略了window.
// 带var
// 变量提升 var a <=> window.a=undefined
console.log(a); // ->undefined
var a = 12;
console.log(a); // ->
console.log(window.a); // ->
// 不带var
console.log(a); // ReferenceError a is not defined 先看他是不是变量,不是,报错
console.log(window.a) //->12
a = 12; // <=> window.a
console.log(window.a) //->12
作用域链
函数执行形成一个私有作用域,保护其中的私有变量,进入私有作用域中首先变量提升(声明过的变量是私有的),然后代码执行
1、执行的时候遇到变量,若为私有,则按照私有处理即可
function fn(){
console.log(a); // undefined 私有变量
var a = 12;
console.log(a); // 12
}
fn();
console.log(a); //全局下没有这个属性 报错
2、如果当前这个变量不是私有的,需要向他的上级作用域进行查找,上级也没有就继续上一级,直到全局作用域,这种查找机制为
作用域链
。
1)如果上级有,在当前进行的操作则都会操作上级的那个变量
2)若上级没有,一直找到window:
变量 = 值 相当于给window设置了一个属性,
console.log(x, y); // undefined, undefined
var x= 10, y = 20;
function fn2() {
console.log(x, y); //undefined, 20
var x = y = 100;
console.log(x, y); // 100(私有) 100(全局)
}
console.log(x, y); // 10 100
只对等号左边的进行变量提升
=: 赋值,左边的是变量,右边都应是值
匿名函数:函数表达式,把函数当作一个值赋给变量或者其他内容
oDiv.onclick = function(){};
// 相当于oDiv.onclick = aaafff111(函数地址)
只对等号左边进行变量提升,右边就是一个值
console.log(fn); // undefined
var fn = function () {
};
console.log(fn); // [Function: fn]
真是项目中应用这个原理,使用函数表达式创建函数
1)只能对等号左边进行提升,变量完成之后,当前函数只是声明了没有定义,执行函数必须放在赋值代码之后
2)可让代码更加严谨
不管条件是否成立,都要进行变量提升
console.log(num); // undefined
console.log(fn); //undefined
if(1 === 1){
console.log(num); // undefined
console.log(fn); // 函数体本身
var num = 12;
function fn() {
}
console.log(num); // 12
console.log(fn); // 函数体本身
}
console.log(fn); // 函数体本身
不管条件是否成立,判断体中出现的var, function都会进行变量提升,,在最新版本中,function只能提前声明,不能定义(因为不确定判断条件是否成立)
[条件不成立]
进不到判断体中,赋值的代码执行不了,之前声明的变量或者函数依然是undefined
[条件成立]
进入判断体的第一件事是把之前声明但未定义的函数先定义,然后以上而下执行代码
但老版浏览器不管条件是否成立,都会进行函数声明加定义
重名处理
在变量提升阶段,若有重名,不会重新进行声明,但函数的定义会有替换(后替换前)
私有变量
1、私有作用域变量提升阶段,声明过的变量或者函数
2、形参
function add(arg1, arg2){
var sum = arg1 + arg2; // sum,arg1, arg2私有变量
return sum;
}
var sum = add(10, 20); // sum公有
上级作用域
函数的上级作用域跟在哪执行的没关系,在哪定义上级作用域就是谁
var n = 1;
function fn(){
console.log(n);
}
fn(); // 1
(~function (){
var n = 2;
fn(); // 1 fn的宿主环境是当前自执行函数形成的私有作用域
})();
var p = 10;
var obj = {
p: 20,
fn: (function () {
var p = 30;
return function () {
console.log(p);
}
})()
};
// 执行的是return那个函数而不是自执行函数
// 函数的上级作用域是自执行函数
obj.fn(); // 30
var p = 10;
var obj = {
p: 20,
fn: (function () {
return function () {
console.log(p);
}
})()
};
obj.fn(); // 10 自执行函数的上级作用域是全局作用域
var p = 10;
var obj = {
p: 20,
fn: (function () {
return function (p) {
console.log(p);
}
})(obj.p)
};
obj.fn(); // 报错 Cannot read property 'p' of undefined
// 自执行函数执行的时候还obj还没有赋值
网友评论