JS中的对象、函数以及作用域
JS中的对象、函数以及作用域
一、对象
1、对象的创建方法
- 构造函数创建法
var o=new Object();
- 字面量创建法
var o={a:1};
2、对象中的值是以键值对的形式存在,每个键对应唯一的值。
键必须是字符串或者Symbol,如果不是则会隐式转换为字符串
key value 键 值
3、对象的存储一共有两种情况,一种是存储数据,一种是存储函数
存储数据的叫做对象的属性,存储函数叫做对象的方法
var o = {a: 1,b: true,c: {d: 10},e: function (){}}
4、对象的引用
console.log(o.a);//用点语法不加双引号
console.log(o[“a”]);
注意:o.a相当于o[“a”]
var o={};
o.a=1;
o[“b”]=2; //往对象o中添加属性和属性值
delete o.c; //删除对象o中的属性,删除属性 ,如果删除的属性不存在,也不会报错
如果键是一个变量,不能使用.语法,必须使用[ ]带入变量
5、遍历对象的属性
console.log(“a” in o);
for(var prop in o){
console.log(prop,o[prop]);
}
// 对象中存储的内容 相互之间是没有关联的
// 如果需要在对象中查找是否有某个键 (“a” in o);
// 如果需要在对象中查找是否有某个值,只能通过for in遍历对象查看每个值是否满足
6、对象存储在堆中,栈中的是对象存储在堆中的引用地址
7、对象赋值
这种方式是赋值了引用地址,因此o和o1是同一个引用地址,同一个对象
修改任何一个,另外变量看到的也是被修改后的结果
var o={a:1};
var o1=o;
o1.a=10;
console.log(o.a);
通过for in遍历对象复制 //浅复制
var o={a:1,b:2};
var o1={};
for(var prop in o){
o1[prop]=o[prop];
}
在对象复制时,对象里面的属性也是一个对象,那么会把这个对象属性的引用地址存到属性值中
只有深复制才可以真正将一个对象的每个属性都复制没有引用关系(深复制查看文章递归)
https://blog.csdn.net/m0_52634705/article/details/115453765
8、练习
//String.fromCharCode() ascii码函数 97-122能输出小写字母a-z
利用这个函数创建一个对象,属性为a,b,c...z 属性值分别为1,2,3,...26
var o=new Object();
for(var i=1;i<27;i++){
o[String.fromCharCode(i+96)]=i;
}
二、垃圾回收机制
// var o={a:1};
// 将栈中变量o设置为null,表示不再引用对象,然后将堆中这个对象的引用列表中这个变量去除
// o=null;
// 如果说一个对象被多个变量引用,只有将所有引用该对象的变量全部设置为null,才可以被垃圾回收
//只有存储引用地址的变量才需要设置null来做垃圾标识
//内存泄漏 大量的不使用的引用对象,没有被标识为null,并且还在不断生成和丢弃
例如:删除堆中的内容,必须先把引用关系设置标识为null
var div=document.getElementById("div1");
div.remove();
div=null;
console.log(div);
三、函数
1、函数的结构形态
function fns(a,b){ }
function 定义函数
fns 函数名,可以自己起,与变量命名相同,且它本身就是一个变量
(a,b) 可以向函数中注入的数据,a,b叫做形参,注入几个数据就要声明几个参数
{} 就是函数执行的语句块
2、函数的执行
fns(4,5) 执行函数就是将函数中的语句块全部运行一遍
fns 函数名
(4,5) 4,5是实参,4将会赋值给a,5将会赋值给b,完全按照参数的顺序赋值
3、函数的参数
- 如果实参数量小于形参数量 那么最后没有赋值形参则是undefined;如果形参属性小于实参数量,那么多出来的实参无法从形参上获取。
- 形参初始值,当实参没有传入对应的值(实参是undefined),默认是形参初始值,当实参传入了值,则为实参的值。
必要的形参定义在函数的前面,非必要的形参定义在后面,大多数的初始值都是针对非必要形参。
形参中ES5不能设置形参初始值,ES6中形参可以设置初始值。
function fn(a,b,c,d=10){console.log(a,b,c,d)}
- ES6中,传入不定数量的参数时都会被放在arg这个数组中,
如果还有其他形参,…arg必须放在形参的最后一个。
function fn(…arg){console.log(arg)}
function fn(a,…arg){console.log(arg)}
4、arguments
- 所有通过执行函数时输入的实参(实参列表)
- arguements.length 表示当前函数内传入的实参数量
- console.log(fn1.length); 通过获取函数的length可以获取到函数的形参数量
- arguments[索引] 可以通过索引值查看对象的数据,索引值从0开始递增,所以最后一个数据的索引值一定是length-1
- arguments.callee 表示当前函数 (ES6中不再使用)
arguments.callee.name 表示当前函数的名字
arguments.callee.caller 调用当前函数的上下文函数 (ES6中不再使用)
function fn1(){
console.log(arguments.callee);
console.log(arguments.callee.name);
console.log(arguments.callee.caller);
}
function fn2(){
fn1();
}
fn2();
fn1();
拓展:匿名递归
function fn1(fn){
fn(0);
}
fn1(function(a){
a++;
console.log(a);
if(a>4) return;
arguments.callee(a);
})
5、return
return作用
- 跳出当前函数,不会执行return后的语句
- 使用return返回一个运行结果,或者一个值,仅能返回一个
当函数执行完成后,如果使用return返回了一个值,将会把返回值赋值给a,如果函数中没有return或者直接return没有返回数据,则a的值为undefined
function fns(a,b){
return 10;
}
var a=fns(4,5)
return用法拓展
- 限制函数传入的参数类型或者其值得范围,来判断是否执行函数还是跳出。
function fn1(a){
if(!a) return;
var s=a+5;
console.log(s);
}
fn1(5);
fn1();
- break跳出循环,执行函数中循环后的语句,
return跳出函数,直接从循环中跳出函数。
function fn(){
var s=0;
for(var i=0;i<10;i++){
s+=i;
if(i===7) return s;
}
console.log("aaa");
}
- 点击运行
var bool=false;
var div1=document.getElementById("div1");
var x=0;
var bool=false;
div1.onclick=function(){
bool=!bool;
}
setInterval(function(){
if(!bool) return;
x++;
div1.style.left=x+"px";
},16)
- 单例模式
var o;
function fn1(){
return o|| (o={});
}
写法二:
function fn1(){
if(!o) o={};
return o;
}
var a=fn1();
var b=fn1();
console.log(a==b);
对象a和b相等!!!
- 工厂模式…批量创建不同的对象
function fn1(name,age,sex){
return {name,age,sex}; //对象的属性名如果和值的变量名相同,可以直接带入
}
var o=fn1("张三",30,"男");
var o1=fn1("李四",20,"男")
- return返回多个数据的两种方法
方法1:返回数组
function fn1(a,b){
a++;
b*=2;
return [a,b];
}
方法2:返回对象
function fn1(a,b){
a++;
b*=2;
return {a,b};
}
var o=fn1(2,4);
console.log(o);
- 闭包(函数里面返回一个函数)
function fn1(a){
return function(b){
console.log(a,b);
}
}
var f=fn1;
f(1)(2);
- 对象中的函数
写在对象方法中的this一般指向对象,写在对象属性中的this是指当前所在的上下文环境中this指向
var a=10;
var obj={
a:1,
b:function(){
console.log(this.a);
}
c:this.a
}
var o=obj;
obj={c:10};
o.b();
console.log(o);
可以通过return this,来让对象中的方法实现连缀
var obj={
a:1,
run:function(){
console.log("run");
return this;
},
play:function(){
console.log("play");
return this;
},
}
obj.run().play();
四、函数的三种创建方式
1、函数创建–命名函数法
命名函数法(声明式函数)
function fn(){}
ES6 类中函数创建
class Box{
play(){
}
}
ES6 对象中函数创建
var obj={
play(){
}
}
注意点1
命名函数法创建的函数,可以在函数所在的script标签的任意位置执行,或者当前函数所在的script标签后面的所有标签中执行。
原因:函数被存储在堆中,在创建script标签时,将命名函数存储在堆中,并且在栈中以函数名做引用。
注意点2
当定义全局变量名且与函数名相同时,如果仅定义没有赋值,函数已经先定义过,所以不覆盖,如果定义并且赋值了,那么变量就会被新的赋值覆盖,原函数无法找到
console.log(fn); fn函数
var fn; 没有赋值,此时fn还是函数
var fn=3;
console.log(fn); //3 原函数会被覆盖
function fn(){}
2、函数创建–匿名函数法
匿名函数法(赋值式函数)
var fn=function(){}
fn()
div.οnclick=function(){}
匿名函数自执行函数,只能执行一次
(function(){
console.log(“aaa”);
})();
~function(){
}();
+function(){
}();
注意点
匿名函数法创建的函数,只能在函数被定义后执行。
3、函数创建–构造函数法
构造函数法
var fn=new Function(“a”,“b”,“var s=a+b;return s”);
相当于
function fn(a,b){
var s=a+b;
return s;
}
缺点:在调用时,浏览器会将这个函数对象内的所有内容转换为代码,这需要消耗大量性能
优点:可以动态生成函数
五、JSON
json 是一种字符串格式,{}中属性名是用双引号引起来的
1、JSON.stringify(对象) 把对象转换成json字符串格式
JSON字符串在转换时会丢弃对象中方法
var obj={
a:1,
b:2,
c:"abc",
e:{
f:10
},
h:function(){
console.log("aaa");
}
}
var str=JSON.stringify(obj);
console.log(str);
2、JSON.parse(json格式的字符串) 将json格式的字符串转换为对象
var str1='{"name":"chang","age":30,"man":true}';
JSON.parse(str1)
六、作用域
1、全局变量
- 全局作用域window
- 函数外使用var定义的变量都是全局变量
- 在任何位置都可以调用全局变量
2、局部变量
- 局部作用域function
- 在函数内使用var定义的变量都是局部变量,就是该函数内的局部变量
- 只能在函数内部调用,不能在函数外或者别的函数中调用
- 局部函数只能在当前函数内定义,并且函数执行完毕后会自动销毁该局部变量
3、作用域的三个机制
变量的定义机制
- 在哪个作用域定义的变量,就是这个作用域的私有变量
- 只能在这个作用域及所有后代作用域中使用
变量的赋值机制
- 如果在函数中给一个变量赋值,而这个变量在全局和局部都没有定义过,会把这个变量定义为全局变量,再次进行赋值
变量的使用机制
- 当访问一个变量时,如果自己的作用域内有,则使用自己的,如果没有则去父级查找,直到全局作用域都没有,就会报错:xxx is not defined
4、注意点
- 只要在函数内任何位置定义了局部变量(使用var在函数内定义的变量),在这个函数中就不能找到同名的全局变量,在局部变量定义之前调用这个局部变量都是undefined
var a=5;
function fn(){
if(a===undefined){
var a=6;
}else{
a=7;
}
console.log(a);
}
fn()
console.log(a);
- 因为全局变量是建立在window中,所以通过window调用
var a=5;
function fn(){
var a=10;
console.log(a+window.a);
}
fn();