12.作用域Scope
12.1作用域概述
为了提高程序的可靠性,减少命名冲突;作用域是在运行时代码中的某些特定部分中变量,函数和对象的可访问性。我们可以这样理解:作用域就是一个独立的地盘,让变量不会外泄、暴露出去。也就是说作用域最大的用处就是隔离变量,不同作用域下同名变量不会有冲突。
下文参考链接
作用域分为:全局作用域、函数作用域、块作用域
12.1.0 作用域与执行上下文
我们知道 JavaScript 属于解释型语言(一边解释一边执行),JavaScript 的执行分为:解释和执行两个阶段,这两个阶段所做的事并不一样:
- 解释阶段
- 词法分析
- 语法分析
- 作用域规则确定
- 执行阶段
- 创建执行上下文(函数执行的环境)
- 执行函数代码
- 垃圾回收
JavaScript 解释阶段便会确定作用域规则,因此作用域在函数定义时就已经确定了,而不是在函数调用时确定,但是执行上下文是函数执行之前创建的。
执行上下文最明显的就是 this 的指向是执行时确定的。而作用域访问的变量是编写代码的结构确定的。
作用域和执行上下文之间最大的区别是: 执行上下文在运行时确定,随时可能改变;作用域在定义时就确定,并且不会改变。
一个作用域下可能包含若干个上下文环境。有可能从来没有过上下文环境(函数从来就没有被调用过);有可能有过,现在函数被调用完毕后,上下文环境被销毁了;有可能同时存在一个或多个(闭包)。同一个作用域下,不同的调用会产生不同的执行上下文环境,继而产生不同的变量的值。
12.1.1全局作用域
整个script标签 或者 一个单独的js文件
12.1.2局部(函数作用域)
在函数内部,只能在函数内部访问
12.2变量的作用域
12.2.1全局变量
注意:
没声明,直接赋值的局部变量会变成全局变量(其实是相当于全局对象的属性,而不是严格意义上的变量,因为变量不可用delete删除。
在全局作用域下,使用var定义的变量不可以delete,没有var 定义的变量可以delete,也就说明全局变量严格来说不是真正的变量,而是全局对象的属性,因为属性可以通过delete删除,而变量不可以。
12.2.2局部变量
函数的形参也可以看作是局部变量,在函数作用域内有效
12.2.3全局和局部的区别
全局变量只有浏览器关闭的时候才会销毁,比较占内存
局部变量,当我们程序执行完毕时就会销毁,更节省内存空间
12.3 JS没有块级作用域,es6才新增
12.3.1块级作用域
块级作用域是{}花括号 if{} for {}
//java中 :
if(true){
int num = 10;
}
print(num); //外面不能访问num
//但是JS中可以:
if(3 < 5){
var num = 10;
}
console.log(num); //10
12.3.2for循环的块级作用域
for循环+块级作用域+异步+事件循环问题待解决
12.4作用域链
内部函数访问外部函数的变量,采取的是链式查找的方式来决定取哪个值(就近原则,类似css层叠性)
13.预解析
13.1预解析概述
13.1.1问题
① console.log(num); //报错 不声明不赋值,直接使用
② console.log(num); var num = 10;//undefined 变量提升,声明提升但不提示赋值操作,所以是undefined
③ function fn() { // 函数提升且是声明的形式 console.log(11); } fn();//写在函数的上面下面都可,因为有函数提升
④ var fun = function fn() { console.log(22); } fun(); //放在下面可
⑤ fun();//放在上面不可,会报错 var fun = function fn() { //变量提升,先预解析声明了fun 但没有把函数赋值给fun console.log(22); }
13.1.2JS引擎运行JS步骤
- 预解析:JS引擎会把JS里面所有的var 还有function声明 提升到当前作用域的最前面
- 代码执行:按照代码书写的顺序,从上往下执行
预解析分为 变量预解析(变量提升)和函数预解析(函数提升)
13.2变量预解析与函数预解析
13.2.1变量预解析
就是把所有的变量声明提升到当前的作用域最前面 不提升赋值操作
13.2.2函数预解析
就是把所有的函数声明提升到当前的作用域最前面 不调用函数
13.3预解析案例
。。。
14.对象
14.1对象概述
14.1.1什么是对象?
一个具体事物(类:明星 对象:周星驰)
是一组无序的相关属性和方法的集合
属性是事物的特征,方法是事物的行为
14.1.2对象的作用
数组可以保存多个值但是无法保存完整信息
对象.属性 = 值;
14.1.3变量、属性、函数、方法总结
- 变量和属性的相似点:他们都是用来存储数据的
不同点:
变量:单独声明并赋值 使用时之间写变量名 单独存在
属性:在对象里面不需要声明 使用时必须是对象名.属性
或者对象[属性]
两种访问方式有区别!
**对象有两种属性:实例属性和静态属性
区别:
- 静态属性指的是构造函数本身的属性,只能通过构造函数.xxx去访问;如Math内置对象身上的属性
- 实例属性通过实例对象.xxx去访问
- 函数和方法的相同点:都是实现某种功能的
不同点:
函数:单独声明 并且调用的 单独存在
方法:在对象里面 调用时必须是 对象名.方法()
14.2创建对象的三种方式
14.2.1利用字面量创建对象
对象字面量:{}
var obj = {}; //创建了一个空的对象;
var obj = {
uname: '小野友树',
age: 18,
sex:'男',
sayHi: function() {
console.log('hi');
}
};
注意:
里面的属性或者方法采用键值对的形式
多个属性或者方法中间用逗号隔开
方法冒号后面跟的是一个function关键字直接声明的匿名函数
使用对象:
调用对象的属性:
① 对象名.属性名 obj.uname
② 对象名['属性名'] obj[uname]
调用对象的方法:
对象名.方法名()
14.2.2利用new + Object构造函数创建对象
var obj = new Object(); // 创建了一个空的对象
obj.name = '小野友树'; // 追加属性
obj.sayHi = function() {}; // 追加方法
注意:
利用等号赋值的方法追加属性和方法
每个属性和方法之间用分号!!!
14.2.3利用function关键字直接声明一个自定义构造函数创建对象
因为前面两种方式一次只能创建一个对象,但当很多属性和方法是相同的时,我们利用函数的方法封装起来,又因为函数封装的不是普通代码而是对象,所以需要构造函数!
相当于自定义一个类(不同的是,类里的方法没有function)。
构造函数就是把对象里面一些相同的属性和方法抽象出来封装到函数里面,用来初始化对象。
语法格式:
function 构造函数名(形参1,形参2) {
this.属性 = 值;
this.方法 = function(sang){};
}
调用:
new 构造函数名(‘实参1’,‘实参2’);//new调用构造函数返回的是一个对象
zxy.方法名(‘实参’);
注意:
- 构造函数名首字母要大写
- 构造函数**不需要return 就能返回结果
- 调用时必须使用**new(原因见14.3)
- 只要调用函数,就创建了一个对象
如:
function Hole(uname,type,blood){
this.name = uname;
this.type = type;
this.blood = blood;
this.attack = function(attacked){
console.log(attacked);
}
}
var lianpo = new Hole('廉颇','力量型',500);
lianpo.attack('近战');
console.log(lianpo);
14.3new关键字
对象是一个具体的事物,构造函数可以看做是类
调用构造函数创建对象的过程,也称为类的实例化
14.3.1new关键字调用构造函数会做的四件事
- 先在内存中创建一个空的对象
- this就会指向这个空的对象(相当于新创建的空的对象调用构造函数)
- 开始执行构造函数中的代码,给这个空对象添加属性和方法
- 返回这个新对象(因此不需要return)
因此,如果不用new调用构造函数而是直接调用函数就不会创建一个新的空对象,相当于是window调用这个构造函数,构造函数内的this指向调用者,此时添加的属性是给window对象。
14.4for…in遍历对象属性
for(var k in obj)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K0GElCjO-1668067157639)(https://note.youdao.com/yws/res/334465/WEBRESOURCE0bc5396803a085a8ca4792559046ffca)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XUApFhwc-1668067157639)(https://note.youdao.com/yws/res/334466/WEBRESOURCEb9f7dd1f76da8872fe38be82a29b4bf6)]
注意:
使用for in 时,变量喜欢写 k 或 key
与for…of的区别:
- for…in k代表键名,
obj[k]
才是键值;如循环数组,for in 输出的是数组的index下标,而for of 输出的是数组的每一项的值。 - for…in 可以遍历对象,for…of 不能遍历对象,只能只能遍历带有iterator接口的,例如Set,Map,String,Array
15.内置对象
15.1内置对象概述
JS中对象分为三类:自定义对象、内置对象、浏览器对象(JS独有的,即window)
内置对象就是JS语言自带的一些对象,这些对象供开发者使用,并提供了一些常用的或是最基本而必要的功能(属性和方法)。
15.2更多详见MDN文档,15的笔记很不全
15.3Math数学对象
15.3.1Math.PI
Math对象不是一个函数对象,所有不需要new来调用;直接使用其中的方法属性即可
console.log(Math.PI);
//一个属性 圆周率
这个内置对象的属性和方法都是静态的
15.3.2Math.max() Math.min()
Math.max(1,99,3); //99
console.log(Math.max(1,99,'3')); //99
Math.max(1,99,'pink'); //NaN 如果参数有不是数字类型的没法转换会返回NaN
Math.max(); // -Infinity 如果没有参数,则返回-Infinity
15.3.3 模拟封装自己的数学对象
里面有PI 、最大值和最小值
// 封装
var myMath = {
PI:3.1415926,
max: function() {
var max = arguments[0];
for(var i = 1;i < arguments.length; i++){
if(arguments[i] > max){ max = arguments[i]; } }
return max; }, min: function() {
var min = arguments[0];
for(var i = 1;i < arguments.length; i++){
if(arguments[i] < min){
min = arguments[i]; } }
return max;
}
} //调用
console.log(myMath.PI);
console.log(myMath.max(1,88,77));
console.log(myMath.min(1,4,8,2));
15.3.2Math.abs()取绝对值
Math.abs(1); //1
Math.abs('1'); //字符串型的'1'会转换成数字型1
Math.abs('pink'); //NaN
15.3.3Math.floor()向下取整
// 往最小了取值
Math.floor(1.1); //1
Math.floor(1.9); //1
15.3.4Math.ceil()向上取整
// 往最大了取值
Math.ceil(1.1); //2
Math.ceil(1.9); //2
15.3.5Math.round()就近取整
Math.round(1.1);//1
Math.round(1.5);//2
Math.round(1.9);//2
Math.round(-1.1);//-1
Math.round(-1.5);//-1 因为.5特殊,往大了取 -1>-2
15.3.6Math.random()随机数
Math.random(); // 返回一个随机的浮点数 范围`[0,1)
得到两个数之间的随机整数,且包含两个数在内
function getRandom (min,max) {
return Math.floor(Math.random() * (max - min + 1) ) + min;
}
console.log(getRandom(1,5));
15.3.7猜数字游戏
function getRandom (min,max) {
return Math.floor(Math.random() * (max - min +1) )+ min;
}
var num2 = getRandom(1,10);
while (true){
var num1 = prompt('请输入您猜的数,1~10之间');
if(num1 > num2){
alert('猜大了,继续猜!');
} else if(num1 < num2){
alert('猜小了,继续猜!');
}else{
alert('猜对了!');
break;
}
}
限制次数加个计数器即可
15.4日期时间对象Date
15.4.1Date()
是构造函数,必须使用new来调用
var date = new Date(); //没有参数,返回系统当前时间年月日时分秒
var date = new Date(2021,11,17) //有参数,数字型 返回的是12月
var date = new Date('2021-11-17 6:6:1');//有参数,字符串型 返回的是11月
15.4.2日期格式化(年月日星期)
var date = new Date();
date.getFullYear(); //返回当前日期的年
date.getMonth(); //返回当前日期的月份-1(0-11),记得+1
date.getDate(); //返回当前日期的日
date.getDay(); //返回当前日期是星期几 周一返回1 周六返回6 周日返回0
// 注意:返回的是阿拉伯数字可以作为索引去取数组里的'星期一'等
周日返回的是0所以在数组里写在最前面
function getDate() {
var date = new Date();
var year = date.getFullYear(); //返回当前日期的年
var month = date.getMonth() + 1; //返回当前日期的月份-1(0-11),记得+1
var dates = date.getDate(); //返回当前日期的日
var arr = ['星期日','星期一','星期二','星期三','星期四','星期五','星期六'];
var day = date.getDay(); //返回当前日期是星期几 周一返回1 周六返回6 周日返回0
return '今天是' + year + '年' + month + '月' + dates + '日' + arr[day];
}
15.4.3日期格式化(十分秒)
date.getHours(); //时
date.getMinutes(); //分
date.getSeconds(); //秒
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P2ix3Kte-1668067157640)(https://note.youdao.com/yws/res/334469/WEBRESOURCE96a1bde633abc0fa567aedb5981078a9)]
15.4.4获取日期的总的毫秒形式(时间戳)
从1970年1月1日 开始计算(世界标准时间)到当前时间总的毫秒数
①用valueOf()或 getTime():
var date = new date();//系统当前时间
date.valueOf(); //当前时间距1970.1.1总的毫秒数
date.getTime(); //当前时间距1970.1.1总的毫秒数
②简单的写法(最常用):
var date = +new Date(); //当前时间距1970.1.1总的毫秒数
③H5新增的方法:
console.log(Date.now());
var date = new Date();
console.log(date.valueOf());
console.log(date.getTime());
// var date = +new Date();
// +new Date()相当于调用 Date.prototype.valueOf()方法
console.log(date);
// console.log(Date.now());
15.4.5 dayjs
15.5数组对象Array
15.5.1翻转数组reverse()
var arr = [1,5,3,4];
arr.reverse();
15.5.2冒泡排序sort()
var arr = [1,7,9,5];
arr.sort();
当出现两位数时,不能这么写所以应该:
var arr = [1,7,9,55,8,11];
arr.sort(function(a,b){
return a - b; //升序的方式排列 //return b - a;降序 }); console.log(arr);
15.5.3数组索引方法indexOf()、lastIndexOf()
返回该数组元素的索引号
var arr = ['red' ,'pink', 'blue'];
arr.indexOf('blue'); // 返回结果2
注意:只返回第一个满足条件的索引号
如果没有,则返回-1
lastIndexOf() 是从后面开始查找
15.5.4数组去重
//两个数组,旧数组就是arr,新数组是空的,把旧数组与新数组比较,新数组没有的放进去!!
// 而可以通过indexOf()可以看出来有没有
function unique(arr){
var newArr = [];
for (var i = 0;i < arr.length; i++){
if(newArr.indexOf(arr[i]) === -1){
newArr.push(arr[i]);
}
}
return newArr;
}
var demo = unique(['c','a','z','a','x','a','x','c','b']);
console.log(demo);
15.5.5数组转换成字符串toString()方法
var newArr = [1,2,3];
newArr.toString(); //'1,2,3' 分隔符就是逗号
15.5.6join(分隔符)方法
将数组作为字符串返回;数组元素将由指定的分隔符分隔,默认分隔符是逗号 (,)。
注意
join(分隔符) 方法不会改变原数组
var arr = ['green','blue','pink'];
var arr1 = arr.join(); //green,blue,pink
var arr2 = arr.join('-'); //green-blue-pink
var arr3 = arr.join('&'); //green&blue&pink
15.5.7concat()
连接两个或多个数组,返回一个新的数组
注意:
不影响原数组
var arr1 = [1,5,7];
var arr2 = [3,33,89];
var arr3 = arr1.concat(arr2);
console.log(arr3); //[1, 5, 7, 3, 33, 89]
console.log(arr1); //[1,5,7]
console.log(arr2); //[3,33,89]
15.5.8slice(start,end)浅拷贝
数组截取,slice()方法选择从给定的 start 参数开始的元素,并在给定的 end 参数处结束,但不包括end。
start:
提取起始处的索引(如果省略则从 0 开始),从该索引开始提取原数组元素。
如果该参数为负数,则表示从原数组中的倒数第几个元素开始提取,slice(-2) 表示提取原数组中的倒数第二个元素到最后一个元素(包含最后一个元素)。
如果start超出原数组的索引范围,则会返回空数组。
end:
可选。整数,指定结束选择的位置。
如果 end 被省略,则 slice 会一直提取到原数组末尾。
如果 end 大于数组的长度,slice 也会一直提取到原数组末尾。
如果该参数为负数, 则它表示在原数组中的倒数第几个元素结束抽取(不包括那一个结束的)。 slice(-2,-1) 表示抽取了原数组中的倒数第二个元素到最后一个元素(不包含最后一个元素,也就是只有倒数第二个元素)。
注意:
不影响原数组,因为返回新数组;但因为是浅拷贝,如果原数组元素里有引用类型的,改变新数组会影响原数组。
15.5.9splice(start[, deleteCount[, item1[, item2[, …]]]])
数组删除/插入新的元素
start:
指定修改的开始位置(从0计数)。
如果超出了数组的长度,则从数组末尾开始添加内容;
如果是负值,则表示从数组末位开始的第几位(从-1计数,这意味着-n是倒数第n个元素并且等价于array.length-n);
如果负数的绝对值大于数组的长度,则表示开始位置为第0位。
deleteCount 可选 整数,表示要移除的数组元素的个数。
如果 deleteCount 大于 start 之后的元素的总数,则从 start 后面的元素都将被删除(含第 start 位)。
如果 deleteCount 被省略了,或者它的值大于等于array.length - start(也就是说,如果它大于或者等于start之后的所有元素的数量),那么start之后数组的所有元素都会被删除。
如果 deleteCount 是 0 或者负数,则不移除元素。这种情况下,至少应添加一个新元素。 item1, item2, … 可选 要添加进数组的元素,从start 位置开始。如果不指定,则 splice() 将只删除数组元素。
返回值:
由被删除的元素组成的一个数组。如果只删除了一个元素,则返回只包含一个元素的数组。如果没有删除元素,则返回空数组。
注意:
会影响原数组
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bM14Oe4T-1668067157640)(https://note.youdao.com/yws/res/334471/WEBRESOURCEea4031dae8958944b25382babd1d4ef0)]
15.6字符串对象String
15.6.1字符串型和字符串对象
let s_prim = 'foo';
let s_obj = new String(s_prim);
console.log(typeof s_prim) // Logs "string"
console.log(typeof s_obj) // Logs "object"
15.6.2基本包装类型(把字符串型的变量包装成字符串对象)
var str = 'andy';
console.log(str.length); //4
console.log(typeof str); //string
对象才有属性和方法,即复杂数据类型才有属性和方法
简单数据类型为什么会有length属性呢?
因为在访问由字面量创建的字符串变量的length属性或者方法时,浏览器会把字符串包装成复杂数据类型,实际上进行了以下操作
//(1) 声明一个临时变量,new调用String构造函数
var temp = new String('andy'); //temp本质是对象
//(2)把临时变量的值给str
str = temp; //因此str本质也是对象
//(3)销毁这个临时变量
temp = null;
然后使用包装类型里面的length属性,用完浏览器会把创建的包装类型销毁
基本数据类型 和 引用数据类型(String、Number、Boolean)这三种都可以进行包装
15.6.3字符串(对象)的值的不可变性
var str = 'andy';
str = 'red';
str的值是其实是字符串的地址,此处说的不可变性指的是andy这个字符串值没有改变,本来str存储的是andy的地址,现在重新赋值了red的地址;即字符串重新赋值这个操作其实是 新开辟了堆空间,改变str的指向,指向这个新的堆空间。
字符串的所有方法都不会改变字符串本身!!!
因此不要随意地拼接字符串!
15.6.4根据字符返回字符位置(不会修改字符串本身)str.indexOf(‘’[,start])
var str = '小野友树';
console.log(str.indexOf('树')); //3
var str1 = '小野友树,树';
console.log(str1.indexOf('要查找的字符',起始的位置));
console.log(str1.indexOf('树',4)); //5
15.6.5查找字符串中某个指定字符出现的位置及次数
var str = "abcoefoxyozzopp";
var index = str.indexOf('o');
var counts = 0;
while(index != -1){
//找到了
console.log(index);
//从找到了的下一个开始继续找
index = str.indexOf('o',index + 1);
//计数器++
counts++;
}
console.log(counts);
15.6.6根据位置返回字符(重点)
// ① str.charAt(index)
var str = 'andy'; str.charAt(3); //y
// ② str.charCodeAt(index)
// 返回相应索引号的字符的 Unicode 值,目的为了判断用户按下了哪个键
// index:一个大于等于 0,小于字符串长度的整数。如果不是一个数值,则默认为 0。
var str = 'andy';
var n = str.charCodeAt(3);
console.log(n); //y的Unicode码 0~65535 2的16次方个数
// ③ str[index] H5新增的
var str = 'andy'; str[0]; //a
15.6.7判断字符串中出现次数最多的字符,并统计其次数
var str = "abcoefoxyozzopp";
var o = {};
for (var i = 0; i<str.length; i++){
var chars = str.charAt(i);
if (o[chars]) { //不能写成o.chars因为chars 其实是字符,不是真正的属性{} 这里是指看o这个对象是否有chars字符
o[chars]++;
} else{ //如果没有,则是第一次出现
o[chars] = 1;
}
}
console.log(o); ///遍历对象
var max = 0;
var ch = '';
for(var k in o){ //k 得到属性名 //o[k] 得到属性值
if (o[k] > max){
max = o[k];
ch = k;
}
}
console.log(max);
console.log('最多的字符是:'+ ch);
15.6.8字符串操作方法(重点)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SQ0PYDP1-1668067157642)(https://note.youdao.com/yws/res/334476/WEBRESOURCE142450872e0047dc1a9466d458c9a6e6)]
15.6.9替换字符串 replace()
把某几个字符替换成另外几个字符
字符串名.replace(‘被替换的字符’,‘替换为的字符’); //注意只会替换第一次遇到的字符
15.6.10有一个字符串’abcofoxzop’ 要求把里面的所有 o 都替换成 *
可以用于敏感词规避
var str1 = 'abcoezopfox';
while (str1.indexOf('o') !== -1){
str1 = str1.replace('o','*'); //只要能找到就继续替换
}
console.log(str1);
15.6.11字符转换为数组 split(‘分隔符’)
分隔符取决于字符串是用什么分隔开的,转换为数组后还是用逗号
//前面学过join 把数组转换为字符串
//split('分隔符')
var str = 'red,pink,blue';
console.log(str.split(','));
var str1 = 'red&pink&blue';
console.log(str1.split('&'));
15.6.12toUpperCase()和toLowerCase()
转换大写/转换小写
15.6.13字符串对象作业
var str = 'abaasdffggghhjjkkgfddsssss34444343';
//1.求字符串长度
console.log(str.length);
//2.取出指定位置的字符,如:0,3,5,9等
console.log(str.charAt(0));
console.log(str.charAt(3));
console.log(str.charAt(5));
console.log(str.charAt(9));
//3.查找指定字符是否在以上字符串中存在,如:i,c,b等
var c = prompt('请输入您要查找的字符:');
if (str.indexOf(c) == -1){
alert('您所查找的字符:' + c + '不存在!');
}else{
alert('您所查找的字符:' + c + '存在!');
}
//4.替换指定的字符,如:g替换为22,ss替换为b等操作方法
var oldChar = prompt('请输入被替换的字符:');
var newChar = prompt('请输入您要替换为什么字符:');
while(str.indexOf(oldChar) !== -1){
str = str.replace(oldChar,newChar);
}
console.log(str);
//5.截取指定开始位置到结束位置的字符串,如:取得1~5的字符串
var start = prompt('请输入从第几个字符开始:');
var end = prompt('请输入到第几个字符结束:');
console.log(str.substring(parseInt(start) - 1,parseInt(end)));
//6.找出以上字符串中出现次数最多的字符和出现的次数
// ①charAt()遍历字符串
// ②把每个字符都存储给对象,相当于对象的属性,这里不能用对象.属性名的方式访问。因为本质还是字符,如果对象具有该属性则+1,不存在则为1
// ③for...in遍历对象属性,得到最大值和该字符
var o = {};
var counts = 0;
for (var i = 0; i < str.length; i++){
if (o[str.charAt(i)]){
o[str.charAt(i)]++;
}else{
o[str.charAt(i)] = 1;
}
}
console.log(o);
var max = 0;
var ch = '';
for (var k in o){
if(o[k] > max){
max = o[k]; //o[k]得到属性值
ch = k; //k得到属性名
}
}
console.log('出现次数最多的字符是:' + ch + ',出现次数为:' + max);
16.JS简单类型与复杂类型
16.1简单数据类型与复杂数据类型
16.1.1简单数据类型
简单数据类型又称基本数据类型或值类型
es5:string、number、boolean、undefined、null
其中null 返回的是一个空对象指针,(是一个地址) typeof null 是 object
null是空的对象那为什么还是值类型捏,因为不是对象,而是打算放对象捏
如果有个变量,以后打算存储为对象,暂时没想好放什么,可以给null
16.1.2复杂数据类型
复杂数据类型又称引用类型
在存储时,变量中存储的仅仅是地址(引用),通过new关键字创建的对象(系统对象、自定义对象),如:Object、Array、Date等在堆中存储。
16.1.3堆和栈
操作系统会把内存分成:
栈(操作系统):由操作系统自动分配释放存放函数的参数值、局部变量的值等。其操作方式类似于数据结构中的栈;简单数据类型存放到栈里。
堆(操作系统):存储复杂数据类型(对象),一般是由程序员分配释放,若程序员不释放,则由垃圾回收机制回收;复杂数据类型存放到堆里。
**复杂数据类型首先在栈里存储地址,地址指向堆里的数据。
16.1.4简单数据类型传参
值传递不会改变实参,其实是把变量在栈空间中的值赋值了一份为形参(深)
16.1.5复杂数据类型传参
p传参给x时,因为p是地址,所有p和x指向同一个堆里的数据(浅)