目录
对象
函数
三种函数定义
1、命名函数定义
2、匿名函数定义
3、构造函数定义
函数参数
函数返回值
对象引用
作用域
声明统一提前,赋值原地不变 (函数声明优先于变量)
回调函数
递归函数
obj ["a"]=30; // obj [ key ]=value; 等价于 obj . a =30; (点语法内不能使用变量,不加引号)
[ ] 内key要求属性名必须是字符型或symbol型,所以必须要有 " "
var a = "ab";
var b=9;
obj [a] =30; // a是一个变量,相当于将"ab"字符串作为obj的属性名
obj[b]=20; // 如果变量内的值不是字符型,则会隐式转换成字符型作为属性名 将变量b中的数字9隐式转换为字符“9”并作为obj的属性名
console.log(obj.c); //obj对象内没有c属性,打印结果为undefined
var n;
var m;
obj[n]=10;
console.log(obj[m]); //打印结果为10
obj[m] => obj["undefined"]
对象被强转为字符串会变成 " [ object object ] "
栈 数据读取写入速度快,但存储内容较少
堆 读取和写入速度慢,但存储内容多
字符型,数值型,布尔型,undefined 都存在栈中 值类型
对象,函数存在堆中 引用类型
var obj={a:1,b:2};
console.log(obj); // 打印的是{a:10,b:2}
obj.a=10;
当点击对象前面的箭头时,才去堆中对应的地址取出数据
引用地址就是指堆中对象存储的地址
var o={a:1};
var o1=o;//将o对象的引用地址赋值给o1
o1.a=10;//因为o1和o是同一个对象,因此修改o1的属性就相当于修改了o的属性
console.log(o);
var o={a:1};
var o1=o;
o={b:2}; //重新改变新地址
o.a=20;
console.log(o1); // {a:1}
console.log(o); // {b:2,a:20}
栈中数据,一旦该变量不再使用,就会被清理掉
alert () 只能展示字符串
内存泄漏 当对象不再使用但对象的引用列表中并没有清除引用关系时,对象依旧会存在于堆中,占用内存,不会被垃圾回收车回收
var i={a:1}; // 变量 i 内存放着对象{a:1} 的地址
var j=i; // 变量 j 内也存着对象{a:1}的地址 变量 i 和 j 都指向对象
如果此时,想要给变量 j 重新赋值,则先需要在对象{a:1}的引用地址中删除 j 对这个对象的引用,在重新赋值
i = null; // 取消引用,释放对象占用的内存
i = 2; // 重新赋值
编译型语言 先编译好,再执行 C / C++
解释型语言 边解释编译,边执行 JS (速度慢,但自由)
函数名 命名规则和变量命名规则一样,都是驼峰式命名,见名思意,函数名表达函数执行的作用
当执行到当前script标签时,预先将当前script标签中所有的命名函数
先存储在堆中,并且在栈中给他定义函数名引用该函数地址
script标签中的函数一旦创建成功,后面的所有script标签中都可以使用该函数,但是再当前script标签前的script标签不能使用
所以最好把装有函数的script写在最前面
function abc(){
console.log("a");
}
var fn=abc; //把函数abc地址赋值传给fn
fn(); // 或者 abc(); => 执行函数
// abc是函数名
// 匿名函数没有名字
// 可以设置给一个变量,或者设置给一个对象的属性
// 命名函数可以在函数定义之前(同一个script内)和之后(script标签可以相同,可以不同)执行,但是匿名函数只能在函数定义后执行
变量匿名函数
var fn=function(){ }
对象方法定义
var obj={
fn:function(){ }
}
自执行函数 // 缺点:只能执行一次,无法再次调用,会造成内存泄漏
(function(){ })();
var fn=new Function("参数1","参数2",..."函数内容")
// 构造函数中,前面的内容就是这个创建函数的所有参数,最后一个内容就是这个函数执行语句块
// 要求所有内容都必须是字符串
// 缺点:构造函数创建函数缺点是运行速度慢,效率低,因为需要将所有的字符串转换为代码
形参 写再函数声明的括号内
实参 写在调用函数的括号内
function abc(n,m){
console.log(n,m);
// 函数名后面的括号中的内容是一种变量,这个变量叫做参数
// 参数名起名规则和变量名类似,但是有时候可以使用下划线来起头(临时变量)
// 如果有多个参数时,就需要使用逗号分隔
// n,m这个形参这个变量的范围是仅在当前函数语句块中
// 在该函数外不要调用该函数的参数
}
abc(5,6);
// 指定参数的值叫做实参(实际的参数内容)
// abc(); //不填入参数也可以执行,但是这种方式就相当于给形参定义值为undefined
// abc(10); //如果只填入一个值,就表示第一个赋值10,第二个仍然赋值undefined
// abc(9,6); //参数传值是按照顺序赋值的
// 执行函数时,需要带入指定参数的值
// 形参有几个,执行函数时实参就需要填几个
// abc(5,6,7); //多传入的7没有参数接收,所以不能被调用
// abc(undefined,10);//不想给n传,只想给m传
如果出现参数的个数不确定 我们可以不定义参数
// 传参时传入栈中的数据
function fn1(n){
n++;
}
var s=1;
fn1(s); //执行fn时,将s变量的值传入到函数fn1中,赋值给n
console.log(s); // s结果为1
// 传参时传入对象
var obj={a:1};
function fn2(o){
o.a++;
}
fn2(obj);
console.log(obj); // a:2
- 解析:
传参传入的是对象,变量是对象,变量的值就是这个对象的引用地址
当传参时将引用地址赋值给o,因此,o和obj的引用地址相同,是同一个对象,
所以修改了o.a值,obj.a也被修改了
// 栈中的数据,赋值时是一个数值、字符、布尔,改变时不会引起引用改变
点击事件
rbn.onclick = clickHandler ; // clickHandler 函数名 => 点击bn时,触发执行函数
bn.onclick = clickHandler ( ) ; //clickHandler()执行函数 =>立即执行函数,然后将函数返回值赋值给bn.onclick
var a = getAge; // 将函数直接赋予变量a,相当于a就是这个函数(函数地址给了变量,引用变量就相当于引用了函数)
var a = getAge(); // 函数执行后,有return就把返回值赋予a,没有return,则把undefined赋予a
//错误示范
fn(){
return 30,40;
} // 40 return不能一次返回多个值,只返回最后的结果
fn(){
return 30; // 返回30 在函数中,当遇到return,就返回并跳出,不再执行return后的代码
return 40;
} // return 除了可以返回数值,还可以用来跳出函数
//正确示范
function getAge(){
var a=30;
var b=40;
return {a:a,b:b}; //return 可以通过对象和数组返回多个值
}
var obj;
function changeObj(o){
if(obj){
obj.a=0;
}
obj=o;
obj.a=10;
}
var o1={};
var o2={};
changeObj(o1); // o1 ={ } 带入函数, 判断if(obj) 由于var obj 为undefined 所以跳过if 执行后面语句
console.log(o1,o2); // a:0 a:10
解析:
把o1={ } 赋值给obj ,给obj { } 添加属性 和属性值
此时 obj === o1;changeObj(o2);
把o2={ } 带入函数 ,判断if(obj) 由于上一次执行函数,obj已经有值 {a:10 } 所以if 为true
并将obj.a 改为0 此时obj 引用的还是o1 所以更改的也是o1 对象的属性值将o2 赋值给obj ,切断之前obj和o1的引用关系 ,并给obj { } 添加属性 和属性值
数值,字符,布尔值,设置给变量以后,存储在栈中
赋值就是单纯的赋值操作两个变量之间没有任何关系其中一个变量改变,另一个变量不会有任何变化
除数值,字符,布尔值以外的其他对象,是存储在堆中的,并且有一个引用地址 (函数也是对象)
赋值时是将这个引用地址赋值给变量的,如果将一个变量赋值给另一个变量,就是将这个引用地址赋值给另一个变量,因此实际这两个变量指向的引用地址完全相同,更改其中一个,就会引起另一个的改变
定义在函数外面的变量,叫做全局变量
定义在函数里面的变量,叫做局部变量
在函数中使用var定义的变量一定是局部变量,参数也是局部变量
function abc(){
a=1; //a是全局变量 相当于window.a
}
abc();
console.log(a);
早期的原生js不需要使用var就可以定义变量,因为他们认为window.变量,所有的变量都是window的属性,而window是可以省略的,所有没有var可以使用,但是到了ES5的后期, 严格要求的代码规范不允许无声明,就使用变量,没有var是不行的,在ES6中window.a也不允许使用了
变量在声明它们的函数体以及这个函数体嵌套的任意函数体内始终可见。即在声明一个变量的前后,你都可以直接使用它,并不会报错。
只有函数声明格式的函数才会存在函数声明提前,比如函数表达式,构造函数,都不存在函数声明提前。
函数创建的三种写法:
- a.函数声明:function fun(a){console.log(a)};(只有这个家伙存在函数声明提前)
- b.函数表达式:var fun = function(a){console.log(a)};
- c.构造函数:var fun = new Function("a",console.log(a));
定义的局部变量不会改变全局变量
在函数中定义了一个和全局变量同名的局部变量,全局变量将无法直接在函数中使用了,局部变量的优先级高
全局变量可以用来存储数据,局部变量一般都是临时使用,函数执行完成后会随着函数的销毁一起被销毁
var a; // undefined类型 如果a原来有值,a还是原来的值 有类型无值
var a=undefined; // undefined类型 强制覆盖了原来a的值 有类型有值
回调函数就是一个参数,将这个函数作为参数传到另一个函数里面,当那个函数执行完之后,再执行传进去的这个函数。这个过程就叫做回调
回调函数可以应用代码模块化或 想要达到一定条件(在某个事件完成或者达到某个时间)再去执行函数
function abc(fn){
fn(3,5); // fn就是回调函数
}
function getSum(a,b){
console.log(a+b);
}
abc(getSum); // 函数也是对象,当带入参数时
setInterval(执行函数,间隔多长时间执行一次) // 定时器 这是一个方法 时间间隔单位为ms
var i=0;
var ids=setInterval(animation,1000); // 在这里animation函数就是回调函数,每间隔1000毫秒回调执行一次这个函数
// 返回一个值,这个值就是当前定时器的标识id
function animation(){
i++;
console.log(i);
if(i>10){
clearInterval(ids); // 清除定时器,ids就是刚才设置定时器时的返回标识id
} // 清除后,定时器就没有了,不会再执行
}
函数自己调用自己
递归如果没有限制的话就会造成堆栈上限溢出
如何写递归可参考: https://blog.csdn.net/xiaohuihui1994/article/details/90648734
当希望遍历具备有深度数据结构时,通常使用递归或者while更为方便
当希望遍历具备广度数据结构时,一般使用for循环遍历
目录
对象
函数
三种函数定义
1、命名函数定义
2、匿名函数定义
3、构造函数定义
函数参数
函数返回值
对象引用
作用域
声明统一提前,赋值原地不变 (函数声明优先于变量)
回调函数
递归函数