1.立即执行函数 return 与 window的区别
1.window写法new Foo.getName
;(function(){var Test = function(){console.log('123');}Test.prototype = {}window.Test = Test;})();Test();Test()//只执行时执行一次
2.return 写法
var Test = (function(){var Test = function(){console.log('345');}return Test;})();Test();Test() //执行两次
2.三目运算
当if()else{}时有,没有else if时,有更简洁的三目运算符
var a = 5;if (a > 0) {console.log('大于0');} else {console.log('小于等于0');}//因为三目运算符是一个语句所以大于0没有;a > 0 ? console.log('大于0'): console.log('小于等于0');//-------------------------------------------------------------------------// 字符串根据ASCII码按位比较 字符串比较不会转换成数字/*** 89 > 9 -> true 执行括号内的语句* '89' > '9' -> false '8'的ASCII码小于'9'的ASCII码 执行:后面的表达式* str = '内层未通过'*/var str = 89 > 9 ? ('89' > '9' ? '通过了': '内层未通过'): '外层未通过';console.log(str); // '内层未通过' 隐式有个return
3.浅拷贝: 浅拷贝就是将对象中的所有元素复制到新的对象中

var person1={name: '张三',age:18,sex:'male',height:180,weight:140}var person2=person1;person2.name = '李四'; //person1.name也变成了李四,不止person2.nameconsole.log(person1,person2)
person1.name也变成了李四,因为person2指向person1,是一个空间地址
浅拷贝对于原始值类型与引用类型的方式有区别
1.原始值类型字段的值被复制到副本中后,在副本中的修改不会影响源对象对应的值
2.引用类型的字段被复制到副本中的却是引用类型的引用,而不是引用的对象
在副本中对子元素引用类型的字段值被修改后,源对象的值也将被修改。
拷贝/复制/克隆 clone
通过在person2中声明赋值person1的所有属性达到复制person1的效果,它俩是不同的空间地址
这是浅拷贝
var person1={name: '张三',age:18,sex:'male',height:180,weight:140}var person2={};console.log(person1['name']);console.log(person1.name);for(var key in person1){person2[key]=person1[key];}person2.name = '李四';console.log(person1,person2)

浅拷贝赋值时也有赋值变量与赋值地址两种情况,当拷贝对象有引用值时,赋值了同一个地址,不是新的空间地址,也会造成修改了同一空间
Object.prototype.num = 1;var person1 = {name:'张三',age:18,sex:'male',height:180,weight:140,son:{first:'Jenney',second:'Lucy',Third:'Jone'},children:{first:{name:'张小一',age:13},second:{name:'张小二',age:14},third:{name:'张小三',age:15},},car:['Benz','Mazda']}var person2 = {};for(var key in person1){person2[key] = person1[key];}person2.name = '李四';person2.son.forth = 'Ben';console.log(person1,person2);//此方法原型链上的属性也会复制到新对象中 num:1
浅拷贝封装函数 排查原型链上继承的属性
Object.prototype.num = 1;var person1 = {name:'张三',age:18,sex:'male',height:180,weight:140,son:{first:'Jenney',second:'Lucy',Third:'Jone'},children:{first:{name:'张小一',age:13},second:{name:'张小二',age:14},third:{name:'张小三',age:15},},car:['Benz','Mazda']}var person2 = clone(person1);function clone(origin,target){var target = target || {}//如果用户不传空对象,不传对象也得解决问题,给予不传时的默认值for(var key in origin){,//筛选origin对象自身的属性,原型上面的属性,不会拷贝,如果没有person2的属性会多个num属性,而不是拷贝到原型里,说明for in会遍历原型链上的属性if(origin.hasOwnProperty(key))target[key] = origin[key];}return target;}person2.name = '李四';person2.son.forth = 'Ben';console.log(person1,person2);//副本更改源数据也会更改
4.深拷贝
深拷贝和上面浅拷贝不同,就是彻底copy一个对象,而不是copy对象的引用。
方法一: 原生写法
一定要讲出来,因为得面试,有时会但说不出来
Object.prototype.num = 1;var person1 = {name:'张三',age:18,sex:'male',height:180,weight:140,son:{first:'Jenney',second:'Lucy',Third:'Jone'},children:{first:{name:'张小一',age:13},second:{name:'张小二',age:14},third:{name:'张小三',age:15},},car:['Benz','Mazda']}var person2 = deepClone(person1);person2.name = '李四';person2.children.forth = {name:'张小四',age:1};person2.car.push('BYD');console.log(person1,person2);function deepClone(origin,target){var target = target || {},toStr = Object.prototype.toString,arrType = '[object Array]';/**/for(var key in origin){if(origin.hasOwnProperty(key)){if(typeof(origin[key]) === 'object' && origin[key] !== null){ // 先测是否是引用型 typeof([]) 等于 object,因为typeof(null)也是object,得排除null的情况// if(toStr.call(origin[key]) === arrType){ //判断是不是数组,因为创建数组和对象的方式不一样// target[key] = [];// }else{// target[key] = {};// }target[key] = toStr.call(origin[key]) === arrType ? [] : {};//为什么要用calldeepClone(origin[key],target[key]); //递归执行}else{target[key] = origin[key];}}}return target;}
方法二:通过JSON方法 弊端不能拷贝对象里的方法
因为JSON里没有方法,只有数据
Object.prototype.num = 1;var person1 = {name:'张三',age:18,sex:'male',height:180,weight:140,son:{first:'Jenney',second:'Lucy',Third:'Jone'},children:{first:{name:'张小一',age:13},second:{name:'张小二',age:14},third:{name:'张小三',age:15},},car:['Benz','Mazda']}var str = JSON.stringify(person1);var person2 = JSON.parse(str);person2.name = '李四';person2.children.forth = {name:'张小四',age:1};person2.car.push('BYD');console.log(person1,person2);
习题
1
function test(){console.log(foo); //undefinedvar foo = 2;console.log(foo); //2console.log(a); //报错}test();
2.
function a(){var test;test();function test(){console.log(1); //1}}a();// AO = {// test: undefined => test(){}// }
3.
var name = '222'var a ={name :'111',say:function(){console.log(this.name);}}var fun = a.say;//相当于/*var fun=function(){console.log(this.name);}*/fun(); // 2222a.say(); // 111var b = {name:'333',say:function(fun){fun();}}b.say(a.say); // 2222//相当于,因为是调用fun函数,fun函数 var fun = a.say; 是这个 在外面定义了在了外面所以是window执行的/*say:function(fun){fun();+function(){console.log(this.name);}()}*/b.say = a.say;/*相当于var b = {name:'333',say:function(){console.log(this.name);}}*/b.say()// 333
4.
function test(){var marty = {name:'marty',printName:function(){console.log(this.name);}}var test1 = {name: 'test1'}var test2 = {name: 'test2'}var test3 = {name:'test3',printName:function(){console.log(this.name);}}test3.printName = marty.printName;marty.printName.call(test1); // test1 this的指向被改成 test1marty.printName.apply(test2); // test2 this的指向被改成 test2marty.printName(); //martytest3.printName(); //test3}test();
5.
var bar = {a:'1'};function test(){bar.a = 'a';//改变了window.bar的值Object.prototype.b='b';return function inner(){console.log(bar.a); // aconsole.log(bar.b); // b 自身没有的属性去上级寻找}}console.log(test());test()();
课后作业
1.写出代码的执行结果跟步骤
function Foo(){getName = function(){console.log(1);}return this;}Foo.getName = function(){console.log(2);}Foo.prototype.getName = function(){console.log(3);}var getName = function(){console.log(4);}function getName(){console.log(5);}Foo.getName();getName();Foo().getName();new Foo.getName();new Foo().getName();new new Foo().getName()//Foo.getName();//2// getName();//4// Foo().getName();//1// new Foo.getName();//2// new Foo().getName();//3// new new Foo().getName()//3
function Foo(){getName = function(){console.log(1);}return this;}Foo.getName = function(){console.log(2);}Foo.prototype.getName = function(){console.log(3);}var getName = function(){console.log(4);}function getName(){console.log(5);}Foo.getName();getName();Foo().getName();getName();new Foo.getName();new Foo().getName();new new Foo().getName()//Foo.getName();//2// getName();//4// Foo().getName();//1// getName();//1// new Foo.getName();//2// new Foo().getName();//3// new new Foo().getName()//3
Foo.getName(); 2
Foo函数也是对象,可以.加属性名,就像之后的原型,Foo.prototype,还有Foo function->.length
Foo.getName = function(){console.log(2);}
相当于
var Foo=new Object();Foo.getName== function () {console.log(2);}
var obj={name:'123',eat:function(){console.log('我在吃饭')}}function Foo(){}Foo.getName();obj.eat()
所以是2,Foo函数也没有运行,getName函数运行了
getName() 4
GO={ getName:undefined
->function(){console.log(5)}
->function(){console.log(4)} }
Foo().getName(); 1
函数运行了,产生AO,getName前面没有var,提升到全局,是全局变量
GO={ getName:undefined
->function(){console.log(5)}
->function(){console.log(4)}
->(do)function(){console.log(1)}}
Foo()是函数调用,返回this指向window,window.getName();
getName(); 1
GO中的getName()函数是function(){console.log(1)},是1
new Foo.getName(); 2
.点的优先级大于new,先执行Foo.getName(),再new 2,打印2
new Foo().getName(); 3
括号优先级比.大,括号先执行,连带着new也执行
相当于(new Foo()).getName();
Foo的this里面并没有getName,但原型上有
var obj={name:'123',eat:function(){console.log('我在吃饭')}}function Foo(){}var foo=new Foo()console.log(foo)var obj1=new obj()console.log(obj1)

var obj={name:'123',eat:function(){console.log('我在吃饭')}}function Foo(){var a=1;this.b=2;}Foo.prototype.c=3;Foo.d = 4var foo=new Foo();console.log(foo);console.log(Foo.d);console.log(foo.d);var obj1=new obj();console.log(obj1);

new new Foo().getName() 3
new ((new Foo()).getName() )
括号>点>new
new 3 打印3
解析: Foo.getName();
函数也是特殊的对象 所以 Foo.getName() 等价于=> 执行 function Foo(){} 这个函数对象下面的getName属性 Foo.getName = function(){
console.log(2);
}
所以输出2
getName();
GO:{
getName: 1.undefined => 2. function getName(){ 3.赋值操作 var getName = function(){
console.log(5); console.log(4);
} }
}
所以输出4;
Foo().getName();
执行 function Foo(){ 因为 getName 没有 var 所以变量提升到全局
getName = function(){
console.log(1);
}
return this;
}
所以 GO:{
getName: 1.undefined => 2. function getName(){ => 3.赋值操作 var getName = function(){
console.log(5); console.log(4);
} }
4. getName = function(){
console.log(1);
}
}
所以输出1
getName();
GO:{
getName: 1.undefined => 2. function getName(){ => 3.赋值操作 var getName = function(){
console.log(5); console.log(4);
} }
4. getName = function(){
console.log(1);
}
}
所以还是输出1
new Foo.getName();
. 跟 new 先运行 . 所以 先运行 Foo.getName = function(){ 输出 2 然后 => new 2
console.log(2);
}
所以还是输出2
new Foo().getName();
new Foo() 跟 . 先执行 new Foo() => 然后执行 this.getName() 本身没有 => 原型 Foo.prototype.getName = function(){
console.log(3);
}
所以输出 3
new new Foo().getName();
new Foo() 跟 . 先执行 new Foo() => 然后执行 this.getName() 本身没有 => 原型 Foo.prototype.getName = function(){
console.log(3);
}
再 new 3
所以还是输出 3
知识点
运算符优先级:
2. 请用 window.prompt 接收用户输入的年份 判断是否是闰年 用三目运算
var year = window.prompt('请输入判断的年份');function isLeapYear(year){return (year % 4 ===0 && year % 100!=0)||(year%400===0)?'是闰年':'不是闰年';}console.log(isLeapYear(year));

