十四、三目运算符、对象克隆、浅拷贝、深拷贝、模块化复习
一、三目运算符
又叫三元运算符,相当于if的三段式:
var a = 5;
if(a > 0){
console.log('大于0');
}else{
console.log('小于等于0');
}
a > 0 ? console.log('大于0') //相当于三段式if
:console.log('小于等于0');
1.1 三目运算符的特点
可以用一个变量去接收三目运算的结果,即三目运算会默认return结果。
var a = 5;
var str = a > 0 ? '大于0' //相当于这里有return
: '小于等于0';
console.log(str); // '大于0'
if判断里可以嵌套if,当然三目运算中也可以嵌套三目运算:
var a = 3,
str = '';
if(a > 0){
if(a > 3){
str = '大于3';
}else{
str = '大于0小于等于3';
}
}else{
str = '小于等于0';
}
console.log(str); //'大于0小于等于3'
用三目运算符来判断:
str = a > 0 ? (
a > 3 ? '大于3'
: '大于0小于等于3'
)
: '小于等于0';
console.log(str); //'大于0小于等于3'
一个关于三目的面试题:
var str = 89 > 9 ?(
'89' > '9' ? '通过了'
: '没通过'
)
: '外层通过了';
console.log(str);
控制台输出'没通过'
。
89>9结果为ture,进入冒号前的三目运算嵌套;比较运算符两侧都为字符串时,从第一个字符开始一 一 比较字符编码ASCII码值;字符8的ASCII码值小于字符9的ASCII码值,比较结束,判断为false,返回冒号后面的结果,即'没通过'
。
二、对象克隆
前导知识:
字面量形式创建的对象实际上是由
new Object()
创建的——>所以字面量对象的__proto__指向了0bject.prototype
——>0bject.prototype是所有对象(非自定义原型)的原型对象
——> 所有对象都继承0bject.prototype上的属性
2.1 浅拷贝
使用for in 遍历对象,把一个对象中的所有属性复制到另一个新对象中。
但是for in 遍历对象时,会把对象的原型链上的自定义属性也复制到新对象中,怎么办呢?
使用hasOwnProperty
只选择源对象自身的属性。
Object.prototype.num = 1;
var person1 = {
name: 'white',
age: 22,
friends: {
first: 'jack',
second: 'perter',
third: 'rose'
}
}
var person2 = {};
for(key in person1){
if(person1.hasOwnProperty(key)){
person2[key] = person1[key]
}
}
console.log(person1);
console.log(person2);
浅拷贝成功,新对象与源对象内容一模一样:
可以对浅拷贝的过程代码进行一个封装:
var person2 = clone(person1, person2);
function clone(origin, target){
var target = target || {}; //提高用户体验
for(key in origin){
if(origin.hasOwnProperty(key)){
target[key] = origin[key]
}
}
return target;
}
给我们的新对象增加一个属性,类型为原始值;
在新对象的引用值内增加一个新属性:
person2.height = 180;
person2.friends.forth = 'ning';
console.log(person1);
console.log(person2);
打印源对象和新对象:
浅拷贝的不足:
浅拷贝之后更改新对象的属性,当属性值为引用值时,会同时更改源对象相应的引用值。新对象和源对象产生了联系!
2.2 深拷贝
解决浅拷贝的不足,使克隆出的新对象与源对象没有任何关系,对新对象的引用值的增删改查都不会影响到源对象。
深拷贝时也可以对拷贝过程进行封装。
深拷贝过程如下:
// 拷贝对象时,都是拷贝数组或者对象
Object.prototype.num = 1;
var person1 = {
name: 'white',
age: 22,
friends: {
first: {
name: 'jack',
age: 22
},
second: {
name: 'perter',
age: 20
},
third: {
name: 'rose',
age: 33
}
},
car: ['Benz', 'Mazda']
}
var person2 = deepClone(person1,person2);
person2.height = 180;
person2.friends.forth = {
name: 'ning',
age: 49
}
person2.car.push('audi');
console.log(person1,person2);
function deepClone(origin, target){
var target = target || {};//用户体验
var toStr = Object.prototype.toString,//缓存方法
arrType = '[object Array]';
for(var key in origin){
// 先判断要复制的是对象自身的属性,不要原型链上的自定义属性
if(origin.hasOwnProperty(key)){
// 判断属性值数据类型是不是引用值 且 引用值不等于null
if(typeof(origin[key]) === 'object' && origin[key] !== null){
if(toStr.call(origin[key]) === arrType){
// 如果引用值是数组,那新对象的属性就声明为一个空数组
target[key] = [];
}else{
// 如果引用值是对象,那新对象的属性就声明为一个空对象
target[key] = {};
}
// 对于引用值的内容进行递归
deepClone(origin[key], target[key])
}else{
// 不是引用值就直接赋值
target[key] = origin[key];
}
}
}
return target;
}
深拷贝后的新对象和源对象没有任何关系。
2.3 JSON深拷贝(不推荐)
使用JSON.stringify
把对象变成字符串,再使用JSON.parse
把字符串变成JSON对象。
Object.prototype.num = 1;
var person1 = {
name: 'white',
age: 22,
friends: {
first: {
name: 'jack',
age: 22
},
second: {
name: 'perter',
age: 20
},
third: {
name: 'rose',
age: 33
}
},
car: ['Benz', 'Mazda']
}
var str = JSON.stringify(person1);
var person2 = JSON.parse(str);
person2.height = 180;
person2.friends.forth = {
name: 'ning',
age: 49
}
person2.car.push('audi');
console.log(person2,person1);
三、模块化复习
3.1 协同作业
每个人单独创建一个作用域,把IIFE返回的函数保存到全局的变量中,等待执行。
每个人写的模块不是立即执行的,需要一些条件后再执行;
模块化的好处也不仅是防止污染全局变量,创建独立的空间;而且把返回的函数保存到GO中等待执行。
/* 实现三个功能:
*打印一个参数值以内能被 3 或 5 或 7 整除的数
*打印斐波那契数列的第 n 位
*打印从 0 到一个数的累加值
*/
//窗口执行完毕以后执行init
// 如果不是这么写,而是init()时是不能执行的
// 因为变量还未被赋值,值为 undefined ,执行到赋值时,才变成函数表达式
window.onload = function(){
init();
}
// 初始化函数
function init(){
console.log(divided(50));
console.log(fbnq(5));
console.log(add(100));
}
// IIFE + 闭包
// 打印一个参数值以内能被 3 或 5 或 7 整除的数
var divided = (function(){
return function(j){
var arr = [];
for(var i = 0; i <= j; i++){
if( i % 3 == 0 || i % 5 == 0 || i % 7 == 0){
arr.push(i);
}
}
return arr;
}
})();
// 打印斐波那契数列的第 n 位
var fbnq = (function(){
return function fn(n){
if(n <= 0){
return 0
}
if(n <= 2){
return 1;
}
return fn(n-1) + fn(n-2);
}
})();
// 打印从 0 到一个数的累加值
var add = (function(){
return function(n){
var sum = 0;
for(var i = 0; i <= n; i++){
sum += i;
}
return sum;
}
})();
3.2 插件
出现即执行。例如轮播图插件,打开网页就执行。
所以插件的标准形式是IIFE+构造函数相关+原型+window暴露到全局/用全局变量接收
等待全局执行或者就直接让它执行。
;(function(){
//函数声明或者函数表达式都可以
var Test = function(){
}
Test.prototype = {
}
window.Test = Test;
})()
总结:模块化的重点在于把IIFE返回的结果保存到GO中等待执行;而插件可以立即执行达到效果。