1.立即执行函数 return 与 window的区别

1.window写法new Foo.getName

  1. ;(function(){
  2. var Test = function(){
  3. console.log('123');
  4. }
  5. Test.prototype = {
  6. }
  7. window.Test = Test;
  8. })();
  9. Test();
  10. Test()//只执行时执行一次

2.return 写法

  1. var Test = (function(){
  2. var Test = function(){
  3. console.log('345');
  4. }
  5. return Test;
  6. })();
  7. Test();
  8. Test() //执行两次

2.三目运算

当if()else{}时有,没有else if时,有更简洁的三目运算符

  1. var a = 5;
  2. if (a > 0) {
  3. console.log('大于0');
  4. } else {
  5. console.log('小于等于0');
  6. }
  7. //因为三目运算符是一个语句所以大于0没有;
  8. a > 0 ? console.log('大于0')
  9. : console.log('小于等于0');
  10. //-------------------------------------------------------------------------
  11. // 字符串根据ASCII码按位比较 字符串比较不会转换成数字
  12. /**
  13. * 89 > 9 -> true 执行括号内的语句
  14. * '89' > '9' -> false '8'的ASCII码小于'9'的ASCII码 执行:后面的表达式
  15. * str = '内层未通过'
  16. */
  17. var str = 89 > 9 ? ('89' > '9' ? '通过了'
  18. : '内层未通过'
  19. )
  20. : '外层未通过';
  21. console.log(str); // '内层未通过' 隐式有个return

image.png

3.浅拷贝: 浅拷贝就是将对象中的所有元素复制到新的对象中

image.png

  1. var person1={
  2. name: '张三',
  3. age:18,
  4. sex:'male',
  5. height:180,
  6. weight:140
  7. }
  8. var person2=person1;
  9. person2.name = '李四'; //person1.name也变成了李四,不止person2.name
  10. console.log(person1,person2)

person1.name也变成了李四,因为person2指向person1,是一个空间地址

浅拷贝对于原始值类型与引用类型的方式有区别
1.原始值类型字段的值被复制到副本中后,在副本中的修改不会影响源对象对应的值
2.引用类型的字段被复制到副本中的却是引用类型的引用,而不是引用的对象
在副本中对子元素引用类型的字段值被修改后,源对象的值也将被修改。

拷贝/复制/克隆 clone
通过在person2中声明赋值person1的所有属性达到复制person1的效果,它俩是不同的空间地址

这是浅拷贝
image.png

  1. var person1={
  2. name: '张三',
  3. age:18,
  4. sex:'male',
  5. height:180,
  6. weight:140
  7. }
  8. var person2={};
  9. console.log(person1['name']);
  10. console.log(person1.name);
  11. for(var key in person1){
  12. person2[key]=person1[key];
  13. }
  14. person2.name = '李四';
  15. console.log(person1,person2)

image.png

浅拷贝赋值时也有赋值变量与赋值地址两种情况,当拷贝对象有引用值时,赋值了同一个地址,不是新的空间地址,也会造成修改了同一空间

  1. Object.prototype.num = 1;
  2. var person1 = {
  3. name:'张三',
  4. age:18,
  5. sex:'male',
  6. height:180,
  7. weight:140,
  8. son:{
  9. first:'Jenney',
  10. second:'Lucy',
  11. Third:'Jone'
  12. },
  13. children:{
  14. first:{
  15. name:'张小一',
  16. age:13
  17. },
  18. second:{
  19. name:'张小二',
  20. age:14
  21. },
  22. third:{
  23. name:'张小三',
  24. age:15
  25. },
  26. },
  27. car:['Benz','Mazda']
  28. }
  29. var person2 = {};
  30. for(var key in person1){
  31. person2[key] = person1[key];
  32. }
  33. person2.name = '李四';
  34. person2.son.forth = 'Ben';
  35. console.log(person1,person2);
  36. //此方法原型链上的属性也会复制到新对象中 num:1

image.png

浅拷贝封装函数 排查原型链上继承的属性

  1. Object.prototype.num = 1;
  2. var person1 = {
  3. name:'张三',
  4. age:18,
  5. sex:'male',
  6. height:180,
  7. weight:140,
  8. son:{
  9. first:'Jenney',
  10. second:'Lucy',
  11. Third:'Jone'
  12. },
  13. children:{
  14. first:{
  15. name:'张小一',
  16. age:13
  17. },
  18. second:{
  19. name:'张小二',
  20. age:14
  21. },
  22. third:{
  23. name:'张小三',
  24. age:15
  25. },
  26. },
  27. car:['Benz','Mazda']
  28. }
  29. var person2 = clone(person1);
  30. function clone(origin,target){
  31. var target = target || {}//如果用户不传空对象,不传对象也得解决问题,给予不传时的默认值
  32. for(var key in origin){,
  33. //筛选origin对象自身的属性,原型上面的属性,不会拷贝,如果没有person2的属性会多个num属性,而不是拷贝到原型里,说明for in会遍历原型链上的属性
  34. if(origin.hasOwnProperty(key))
  35. target[key] = origin[key];
  36. }
  37. return target;
  38. }
  39. person2.name = '李四';
  40. person2.son.forth = 'Ben';
  41. console.log(person1,person2);
  42. //副本更改源数据也会更改

image.png

4.深拷贝

深拷贝和上面浅拷贝不同,就是彻底copy一个对象,而不是copy对象的引用。

方法一: 原生写法

一定要讲出来,因为得面试,有时会但说不出来

  1. Object.prototype.num = 1;
  2. var person1 = {
  3. name:'张三',
  4. age:18,
  5. sex:'male',
  6. height:180,
  7. weight:140,
  8. son:{
  9. first:'Jenney',
  10. second:'Lucy',
  11. Third:'Jone'
  12. },
  13. children:{
  14. first:{
  15. name:'张小一',
  16. age:13
  17. },
  18. second:{
  19. name:'张小二',
  20. age:14
  21. },
  22. third:{
  23. name:'张小三',
  24. age:15
  25. },
  26. },
  27. car:['Benz','Mazda']
  28. }
  29. var person2 = deepClone(person1);
  30. person2.name = '李四';
  31. person2.children.forth = {
  32. name:'张小四',
  33. age:1
  34. };
  35. person2.car.push('BYD');
  36. console.log(person1,person2);
  37. function deepClone(origin,target){
  38. var target = target || {},
  39. toStr = Object.prototype.toString,
  40. arrType = '[object Array]';
  41. /*
  42. */
  43. for(var key in origin){
  44. if(origin.hasOwnProperty(key)){
  45. if(typeof(origin[key]) === 'object' && origin[key] !== null){ // 先测是否是引用型 typeof([]) 等于 object,因为typeof(null)也是object,得排除null的情况
  46. // if(toStr.call(origin[key]) === arrType){ //判断是不是数组,因为创建数组和对象的方式不一样
  47. // target[key] = [];
  48. // }else{
  49. // target[key] = {};
  50. // }
  51. target[key] = toStr.call(origin[key]) === arrType ? [] : {};//为什么要用call
  52. deepClone(origin[key],target[key]); //递归执行
  53. }else{
  54. target[key] = origin[key];
  55. }
  56. }
  57. }
  58. return target;
  59. }

image.png

方法二:通过JSON方法 弊端不能拷贝对象里的方法

因为JSON里没有方法,只有数据

  1. Object.prototype.num = 1;
  2. var person1 = {
  3. name:'张三',
  4. age:18,
  5. sex:'male',
  6. height:180,
  7. weight:140,
  8. son:{
  9. first:'Jenney',
  10. second:'Lucy',
  11. Third:'Jone'
  12. },
  13. children:{
  14. first:{
  15. name:'张小一',
  16. age:13
  17. },
  18. second:{
  19. name:'张小二',
  20. age:14
  21. },
  22. third:{
  23. name:'张小三',
  24. age:15
  25. },
  26. },
  27. car:['Benz','Mazda']
  28. }
  29. var str = JSON.stringify(person1);
  30. var person2 = JSON.parse(str);
  31. person2.name = '李四';
  32. person2.children.forth = {
  33. name:'张小四',
  34. age:1
  35. };
  36. person2.car.push('BYD');
  37. console.log(person1,person2);

习题

1

  1. function test(){
  2. console.log(foo); //undefined
  3. var foo = 2;
  4. console.log(foo); //2
  5. console.log(a); //报错
  6. }
  7. test();

2.

  1. function a(){
  2. var test;
  3. test();
  4. function test(){
  5. console.log(1); //1
  6. }
  7. }
  8. a();
  9. // AO = {
  10. // test: undefined => test(){}
  11. // }

3.

  1. var name = '222'
  2. var a ={
  3. name :'111',
  4. say:function(){
  5. console.log(this.name);
  6. }
  7. }
  8. var fun = a.say;
  9. //相当于
  10. /*
  11. var fun=function(){
  12. console.log(this.name);
  13. }
  14. */
  15. fun(); // 2222
  16. a.say(); // 111
  17. var b = {
  18. name:'333',
  19. say:function(fun){
  20. fun();
  21. }
  22. }
  23. b.say(a.say); // 2222
  24. //相当于,因为是调用fun函数,fun函数 var fun = a.say; 是这个 在外面定义了在了外面所以是window执行的
  25. /*
  26. say:function(fun){
  27. fun();
  28. +function(){
  29. console.log(this.name);
  30. }()
  31. }
  32. */
  33. b.say = a.say;
  34. /*
  35. 相当于
  36. var b = {
  37. name:'333',
  38. say:function(){
  39. console.log(this.name);
  40. }
  41. }
  42. */
  43. b.say()// 333

image.png

4.

  1. function test(){
  2. var marty = {
  3. name:'marty',
  4. printName:function(){
  5. console.log(this.name);
  6. }
  7. }
  8. var test1 = {
  9. name: 'test1'
  10. }
  11. var test2 = {
  12. name: 'test2'
  13. }
  14. var test3 = {
  15. name:'test3',
  16. printName:function(){
  17. console.log(this.name);
  18. }
  19. }
  20. test3.printName = marty.printName;
  21. marty.printName.call(test1); // test1 this的指向被改成 test1
  22. marty.printName.apply(test2); // test2 this的指向被改成 test2
  23. marty.printName(); //marty
  24. test3.printName(); //test3
  25. }
  26. test();

5.

  1. var bar = {
  2. a:'1'
  3. };
  4. function test(){
  5. bar.a = 'a';//改变了window.bar的值
  6. Object.prototype.b='b';
  7. return function inner(){
  8. console.log(bar.a); // a
  9. console.log(bar.b); // b 自身没有的属性去上级寻找
  10. }
  11. }
  12. console.log(test());
  13. test()();

image.png

课后作业

1.写出代码的执行结果跟步骤

  1. function Foo(){
  2. getName = function(){
  3. console.log(1);
  4. }
  5. return this;
  6. }
  7. Foo.getName = function(){
  8. console.log(2);
  9. }
  10. Foo.prototype.getName = function(){
  11. console.log(3);
  12. }
  13. var getName = function(){
  14. console.log(4);
  15. }
  16. function getName(){
  17. console.log(5);
  18. }
  19. Foo.getName();
  20. getName();
  21. Foo().getName();
  22. new Foo.getName();
  23. new Foo().getName();
  24. new new Foo().getName()
  25. //Foo.getName();//2
  26. // getName();//4
  27. // Foo().getName();//1
  28. // new Foo.getName();//2
  29. // new Foo().getName();//3
  30. // new new Foo().getName()//3
  1. function Foo(){
  2. getName = function(){
  3. console.log(1);
  4. }
  5. return this;
  6. }
  7. Foo.getName = function(){
  8. console.log(2);
  9. }
  10. Foo.prototype.getName = function(){
  11. console.log(3);
  12. }
  13. var getName = function(){
  14. console.log(4);
  15. }
  16. function getName(){
  17. console.log(5);
  18. }
  19. Foo.getName();
  20. getName();
  21. Foo().getName();
  22. getName();
  23. new Foo.getName();
  24. new Foo().getName();
  25. new new Foo().getName()
  26. //Foo.getName();//2
  27. // getName();//4
  28. // Foo().getName();//1
  29. // getName();//1
  30. // new Foo.getName();//2
  31. // new Foo().getName();//3
  32. // new new Foo().getName()//3

Foo.getName(); 2
Foo函数也是对象,可以.加属性名,就像之后的原型,Foo.prototype,还有Foo function->.length

  1. Foo.getName = function(){
  2. console.log(2);
  3. }

相当于

  1. var Foo=new Object();
  2. Foo.getName== function () {
  3. console.log(2);
  4. }
  1. var obj={
  2. name:'123',
  3. eat:function(){
  4. console.log('我在吃饭')
  5. }
  6. }
  7. function Foo(){}
  8. Foo.getName();
  9. 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,但原型上有

  1. var obj={
  2. name:'123',
  3. eat:function(){
  4. console.log('我在吃饭')
  5. }
  6. }
  7. function Foo(){}
  8. var foo=new Foo()
  9. console.log(foo)
  10. var obj1=new obj()
  11. console.log(obj1)

image.png

  1. var obj={
  2. name:'123',
  3. eat:function(){
  4. console.log('我在吃饭')
  5. }
  6. }
  7. function Foo(){
  8. var a=1;
  9. this.b=2;
  10. }
  11. Foo.prototype.c=3;
  12. Foo.d = 4
  13. var foo=new Foo();
  14. console.log(foo);
  15. console.log(Foo.d);
  16. console.log(foo.d);
  17. var obj1=new obj();
  18. console.log(obj1);

image.png

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 接收用户输入的年份 判断是否是闰年 用三目运算

  1. var year = window.prompt('请输入判断的年份');
  2. function isLeapYear(year){
  3. return (year % 4 ===0 && year % 100!=0)||(year%400===0)?'是闰年':'不是闰年';
  4. }
  5. console.log(isLeapYear(year));