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.name
console.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 ? [] : {};//为什么要用call
deepClone(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); //undefined
var foo = 2;
console.log(foo); //2
console.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(); // 2222
a.say(); // 111
var 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的指向被改成 test1
marty.printName.apply(test2); // test2 this的指向被改成 test2
marty.printName(); //marty
test3.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); // a
console.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 = 4
var 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));