一、对象
对象就是一个容器,容器中存储了若干个元素,这些元素都通过名称和值这种方式存储
- 名称–>key 值–>value 键值对
- 任何数据结构都无法超越对象的键值查找速度
- key 的类型必须是字符型或者是symbol
通过对象的属性访问值,有两种方法
- obj.a=10; //key必须是字符,而且必须是确定的字符
- obj[“a”]=10; //key可以是任何类型,但是都会被隐式转换成字符串,也可以是变量
var key="names";
obj[key]="xie"; //如果给入的是变量,就会将变量的值作为对象的key
obj.key="xie";// undefined 以点语法使用属性时,默认属性就是字符串,无法使用变量
obj["key"]="xie";
console.log(obj.key);
var obj={a:1,b:2};
//对象没有元素个数,也就是说对象没有长度,无法确定对象中存在的个数
- 在其它语言中对象的存储是无序的,当遍历时对象的属性是随机遍历
- 对于JS来说 属性是按照属性添加的先后顺序遍历的
prop是一个变量。用变量来遍历obj的属性时,要加[]
for(var prop in obj){
//判断某个属性是不是在obj中
//遍历对象的属性名 按照添加属性顺序
console.log(prop,obj[prop]);
}
var o={a:1,b:2};
var o1=o; //将o的引用地址赋值给o1,
o.a=10; //将对象中a属性的值修改
//o={a:10,b:2}; 这是重新将另一个对象的引用对象地址给了o,所有现在的o!=o1
console.log(o1);//控制台中的属性值顺序是按添加属性的顺序打印的,而点击△object后展出的顺序是按ASCII码排列的
var o={a:1,b:2};
var o1=o;
console.log(o); //第一次打开浏览器时,打印的是引用地址,点开△object才会取堆中拿到这个引用地址,值也会打印,刷新页面,先打印缓存中的内容,但花括号中a的值还是1,而点△object从堆中取值a为10;
o.a=10;
堆栈
- 栈相当于内存,脑干 ;堆相当于硬盘,大脑
- 变量名存储在栈中,在栈中变量名通常对应一个值,这个值的类型有很多种
- 只有字符型/数值型/布尔型/undefined/symbol型直接可以以值的方式存储在这个栈的变量名对应之后
- 除上述外其它类型都是引用类型,引用类型都存储在堆中,并且在堆中以一个引用地址来存储值
- 这种引用类型在栈中使用变量名后面存储堆中对应的引用地址
//单链表 二叉树
var o={value:1,left:{value:2,left:{value:4},right:{value:5}},right:{value:3,left:{value:6},right:{value:7}}}
内存泄漏和垃圾回收
var o={a:1};
o={a:2};//{a:1}成垃圾
内存泄漏
指在运行过程中不断创建对象,并且这些对象不再做垃圾回收,就会造成内存泄漏
垃圾回收
当一个对象不再使用时,将这个对象的所有引用地址赋值的所有变量全部设置为null,当内存超出一定值,系统会自动将这些没有引用的对象回收
var o={a:1};
o=null;//解除引用关系
o={a:2};
//dispose()
垃圾管理池 flag
delete
var o={a:1,b:2};
delete o.a;
console.log(o);//{b:2}
var a=10;
console.log(window.a);
delete window.a;//无法删除 所有变量都是window属性,但是无法使用delete属性
console.log(a);
深复制和浅复制
- 对象浅复制 只复制一层,复制c时复制的是引用地址,无法将地址中的值复制到另一个对象
- 当把对象的每一层都完成复制,就叫做对象深复制
浅复制
1. var o={a:1,b:2,c:{value:2}};
var o1={};
for(var prop in o){
o1[prop]=o[prop];
}
o.a=10;
o.c.value=100;
console.log(o1); //o1中的c值变为100
2. var o1={};
Object.assign(o1,o);//Object.assign(目标,源) 浅复制
o.a=10;
o.c.value=100;
console.log(o1);
3.var o1=Object.assign({},o);//把o复制到空对象上,并且返回给o1
对象深复制
var obj={
a:1,b:2,c:3,d:{e:10},f:undefined
}
var o=JSON.parse(JSON.stringify(obj));
obj.d.e=100;
console.log(o); //e的值不会改变
二、函数
创建函数
- 函数就是语句块,在适当的时机执行函数,就可以完成对于该语句块的执行
- 函数要简洁、功能单一
- 格式:function 函数名(参数1,参数2…){ 语句块 }
1. 命名函数的创建
function fn(){
console.log("aaa");
}
//函数基于对象创建,所以函数也是方法,typeof时函数是function
//函数存在堆中,当函数所在的script标签被执行时,先将函数放在堆中,然后再栈中创建变量(函数名)引用堆中的函数地址
//引用完成后,函数名的变量就会自动生成,生成后再执行当前script后面的语句
//命名函数可以写在当前标签的任意位置,都可以在任意位置调用
2.匿名函数
当代码执行到这里时,创建一个匿名函数赋值给一个变量
只有定义以后才可以调用,之前是不可以的
var fn1=function(){
console.log("bbb");
}
fn1();
自执行匿名函数
(function(){
console.log("aaa");
})();
~function(){
console.log("aaa");
}();
+function(){
console.log("aaa");
}();
3.构造函数创建法
里面的所有内容都是字符串,没有提示,其次函数再创建时会将字符串转换为代码,消耗极大
使用环境其中之一:后端给前端一段js字符串代码取执行
var fn=new Function("a","b","console.log(a+b)");
function fn(a,b){
console.log(a+b);
}
拷贝:
for(var prop in obj){
if(typeof obj[prop]==="function"){
obj[prop]=obj[prop].toString();
}
}
console.log(JSON.stringify(obj));
fn(5,8);
函数作用域
- 全局变量 当在函数内部使用全局变量,被调用一次变量修改一次
- 局部变量 当函数执行完成后,局部变量会自动销毁。局部变量无法根据函数执行次数保留累计变量值
var a=3;
function fn(c){
//如果函数中定义了与外部的全局变量相同的名称时,当前函数中无法直接使用全局变量
//c是参数,也是局部变量
//var a; 变量提升
var a=5;
console.log(a+window.a+fn.a); //5+3+10 局部优先
}
fn.a=10;
fn();
1. var a=10;
function fns(){
if(a<10) var a=10;
else console.log(a);
}
fns();//undefined 在函数中只要出现var,变量提升到函数顶部
2. var a=5;
var a=function(a,a){
console.log(a);
var a=7;
console.log(a);
}
a(4,6); //报错
3.function fns(o){
o=100;
}
var a=10;
fns(a);
console.log(a); //10
4. function fns(o){
o={a:2};
}
var a={a:1};
fns(a);
console.log(a);// {a:1} 将参数a带入函数的适合相当于 var o=a;然后再给o重新赋值{a:2};
5.function fns(o){
o.a=10;
}
var a={a:1};
fns(a);
console.log(a);
6. var a = {n: 1}
var b = a
a.x = a = {n: 2}
求 a.n b.n a.x b.x
2 1 undefined {n:2}
this指向
1.对象属性上的this和对象中函数中的this
var a=10;
var obj={
// 写在属性上的this,一边创建该对象,一边设置值,对象还没有创建完成,所以this仍然指向window
a:this.a,
b:function(){
// console.log(a);//全局a
// console.log(obj.a);
// 写在函数中的this,函数调用时,对象本身一定是创建完成的,谁调用当前函数,this就是谁
console.log(this.a);//谁调用当前b函数,this就是谁
},
c:{
a:this.a,
b:function(){
console.log(this.a);
}
}
}
var o=obj;
obj={c:10};
o.b(); //10 undefined 10 this指对象o
o.c.b(); //10 10 this指c对象
2.单函数内部的this指向windows
3.回调函数中的this
》不管回掉函数中原this指向什么,现在this统一指向window
》当使用arguments完成回调函数的执行时,不管回调函数中this原指向什么,在这里统一指向当前的arguments
function fn1(f){
f();//回调 ,不管回调的函数中原this指向什么,现在统一指向window
}
function fn3(f){
arguments[0]();//当使用arguments完成回调函数的执行时,不管回调函数中this原指向什么,在这里统一指向当前的arguments
}
var obj={
a:1,
fn2:function(){
console.log(this);//window
}
}
obj.fn2(); //this指向obj
fn1(obj.fn2); //this指向window
fn3(obj.fn2); //this指向Arguments
参数
- 函数中的参数是局部变量,可以通过执行函数时将值传递给函数,赋予对应位置的参数
- 参数分为形参和实参
- 函数定义中的参数叫形参,执行函数传入的数据叫实参
- 实参赋值给形参时,按照实际顺序一一赋值
- 参数是弱类型,ES5中参数不能设置初始值,参数必须按顺序给入,参数在传递时如果不需要填入时,用逗号分隔
- 函数的长度为形参数量 函数名.length
- 参数如果有不定数量,在ES5中无法设置不定数量,但是在ES5函数中会自动创建一个arguments
- 实参的数量arguments.length
求最大值
function max(){
// console.log(arguments.length);//实参的数量
if(arguments.length===0) return undefined;
if(arguments.length===1) return arguments[0];
var max=arguments[0];
for(var i=1;i<arguments.length;i++){
max=max<arguments[i] ? arguments[i] : max;
}
console.log(max);
}
max(5,6,7,10,20);
function fn2(a,b){
console.log(arguments.callee);//当前执行的函数 fn2
console.log(arguments.callee.caller);//在那个函数中执行了fn2
console.log(fn2.caller);
}
fn1(fn2);
结果:
ƒ fn2(a,b){
console.log(arguments.callee);//当前执行的函数 fn2
console.log(arguments.callee.caller);//在那个函数中执行了fn2
console.log(fn2.caller);
}
ƒ fn1(f){
f(3,5);
}
ƒ fn1(f){
f(3,5);
}