Practical Training JQuery中闭包、深拷贝、浅拷贝
jQuery的本质的JavaScript 、JavaScript做不了的jQuery也可以做
jQuery直接操作文档
现在比较流行的是:以数据为驱动
数据驱动:
数据库驱动是不同数据库开发商(比如oracle mysql等)为了某一种开发语言环境(比如java)能够实现统一的数据库调用而开发的一个程序,他的作用相当于一个翻译人员,将Java语言中对数据库的调用语言通过这个翻译翻译成各个种类的数据库自己的数据库语言,当然这个翻译(数据库驱动)是由各个开发商针对统一的接口自定义开发的、
另:
所谓“数据驱动”,就是企业内部运转的一个接一个的“数据循环”,通过数据分析和价值发现改善客户、产品、基础设施、盈利方式等核心环节,形成独特的竞争优势,最终实现整个企业乃至供应链的快速运转。这样的企业就是数据驱动的企业,我们称为“数据飞轮”。判断一个企业内部是否形成了“数据飞轮”,就要看企业运行的各个环节是不是都有数据的支撑。
(1)客户模块:主要是“关系数据飞轮”:企业与客户的连接方式发生了根本的转变,传统的渠道商和中间商被逐渐弱化,由客户直接连接品牌成为未来发展的趋势;人群就是传播,有界面的地方就需要营销,产品和消费者接触的界面就是营销点;企业可依据O2O
的23
个接触点的关系数据,进行全渠道管理、全媒体营销。
(2)产品模块:主要是“价值数据飞轮”:产品的研发设计源自用户反馈、参与的数据;产品价值中,信息部分的价值越来越高,如特斯拉机械部分成本不到30%,但信息部分的成本很高;一定要将产品设计分为标品和非标品两类,把标品价格压低,和对手竞争,提高非标品价格,获得利润。
(3)基础设施模块:在基础设施模块,主要是“平台数据飞轮”:内外部资源平台化,基础设施以前是企业自己的生产平台,这些年可以是自己的,也可以是合作伙伴的,甚至是社会化的;从弹性制造到众包众筹,甚至物流,都可以视为企业的数据资源平台。
(4)盈利模式模块:
盈利模式悄然发生改变:营收方式的弹性化和交易方式去风险化,收费到免费,一次交易到多次交易,直接付费到第三方付费,这就意味着消费者在买东西时会不再犹豫,多次交易也没有后顾之忧。由此形成了企业“盈利模式飞轮”。
闭包:
一个函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包(closure)。也就是说,闭包让你可以在一个内层函数中访问到其外层函数的作用域。在 JavaScript 中,每当创建一个函数,闭包就会在函数创建的同时被创建出来。
语法作用域:
function init() { var name = "Mozilla"; // name 是一个被 init 创建的局部变量 function displayName() { // displayName() 是内部函数,一个闭包 alert(name); // 使用了父函数中声明的变量 } displayName(); } init();
init()
创建了一个局部变量 name
和一个名为 displayName()
的函数。displayName()
是定义在 init()
里的内部函数,并且仅在 init()
函数体内可用。请注意,displayName()
没有自己的局部变量。然而,因为它可以访问到外部函数的变量,所以 displayName()
可以使用父函数 init()
中声明的变量 name
。
使用这个 JSFiddle 链接运行该代码后发现, displayName()
函数内的 alert()
语句成功显示出了变量 name
的值(该变量在其父函数中声明)。这个词法作用域的例子描述了分析器如何在函数嵌套的情况下解析变量名。词法(lexical)一词指的是,词法作用域根据源代码中声明变量的位置来确定该变量在何处可用。嵌套函数可访问声明于它们外部作用域的变量。
具体可参考:闭包 - JavaScript | MDN (mozilla.org)
深拷贝和浅拷贝:
深拷贝和浅拷贝是只针对Object和Array这样的引用数据类型的 。 浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。 但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。
具体区别:
深拷贝和浅拷贝最根本的区别在于是否真正获取一个对象的复制实体,而不是引用。
假设B复制了A,修改A的时候,看B是否发生变化:
如果B跟着也变了,说明是浅拷贝,拿人手短!(修改堆内存中的同一个值)
如果B没有改变,说明是深拷贝,自食其力!(修改堆内存中的不同的值)
浅拷贝(shallowCopy)只是增加了一个指针指向已存在的内存地址,
深拷贝(deepCopy)是增加了一个指针并且申请了一个新的内存,使这个增加的指针指向这个新的内存,
使用深拷贝的情况下,释放内存的时候不会因为出现浅拷贝时释放同一个内存的错误。
浅复制:仅仅是指向被复制的内存地址,如果原地址发生改变,那么浅复制出来的对象也会相应的改变。
深复制:在计算机中开辟一块新的内存地址用于存放复制的对象。
浅拷贝实例:
//此递归方法不包含数组对象
var obj = { a:1, arr: [2,3] };
var shallowObj = shallowCopy(obj);
function shallowCopy(src) {
var newobj = {};
for (var prop in src) {
if (src.hasOwnProperty(prop)) {
newobj[prop] = src[prop];
}
}
return newobj;
}
因为浅复制只会将对象的各个属性进行复制,并不会进行递归复制,而JavaScript存储对象是存地址的,所以浅复制会导致Obj.arr和shallowObj.arr指向同一块内存地址:
导致的结果就是:
shallowObj.arr[1] = 5;
console.log(obj.arr[1]); //5
深拷贝实例:
var obj = { a:1, arr: [1,2], nation : '中国', birthplaces:['北京','上海','广州'] }; var obj2 = {name:'杨'}; obj2 = deepCopy(obj,obj2); console.log(obj2); //深复制,要想达到深复制就需要用递归 function deepCopy(o, c){ var c = c || {}; for(var i in o){ if(typeof o[i] === 'object'){ if(o[i].constructor === Array){ //这是数组 c[i] = []; }else{ //这是对象 c[i] = {}; } deepCopy(o[i], c[i]); }else{ c[i] = o[i]; } } return c; }
而深复制则不同,它不仅将原对象的各个属性逐个复制出去,而且将原对象各个属性所包含的对象也依次采用深复制的方法递归复制到新对象上。这就不会存在obj和shallowObj的arr属性指向同一个对象的问题。
这样obj1和obj2分别拥有不同的内存地址,两边的值改变互不影响。
具体可参考:深拷贝和浅拷贝的区别 - 割肉机 - 博客园 (cnblogs.com)
具体题例:53. 下面这段代码想要循环延时输出结果 0 1 2 3 4,请问输出结果是否正确,如
果不正确,请说明为什么,并修改循环内的代码使其输出正确结果
for (var i = 0; i < 5; ++i) { setTimeout(function () { console.log(i + ' '); }, 100); }
不能输出正确结果,因为循环中 setTimeout 接受的参数函数通过闭包访问变量 i。javascript 运
行环境为单线程,setTimeout 注册的函数需要等待线程空闲才能执行,此时 for 循环已经结束,i
值为 5.五个定时输出都是 5 修改方法:将 setTimeout 放在函数立即调用表达式中,将 i 值作为
参数传递给包裹函数,创建新闭包
for (var i = 0; i < 5; ++i) { (function (i) { setTimeout(function () { console.log(i + ' '); }, 100); }(i)); }
讲解:
// var 会生成一堆东西 for (var i = 0; i < 5; i++) { // 声明时同时调用、如果不加()会报语法错误 // (i) 形参 可更改 (function(i){ setTimeout(function(){ console.log(i); },1000) // (i) 实参 }(i)); } // 闭包:函数--把东西给函数 成为闭包
54. 编写 javascript 深度克隆函数 deepClone
function deepClone(obj) { var _toString = Object.prototype.toString; // null, undefined, non-object, function if (!obj || typeof obj !== 'object') { return obj; } // DOM Node if (obj.nodeType && 'cloneNode' in obj) { return obj.cloneNode(true); } // Date if (_toString.call(obj) === '[object Date]') { return new Date(obj.getTime()); } 闭包-方法 也相当于自己执行函数 这个输出的始终为5 因为settimeout 是直接输出的意思 相当于你传给i的数值是什么 输出的也是什么 // RegExp if (_toString.call(obj) === '[object RegExp]') { var flags = []; if (obj.global) { flags.push('g'); } if (obj.multiline) { flags.push('m'); } if (obj.ignoreCase) { flags.push('i'); } return new RegExp(obj.source, flags.join('')); } var result = Array.isArray(obj) ? [] : obj.constructor ? new obj.constructor() : {}; for (var key in obj ) { result[key] = deepClone(obj[key]); } return result; } function A() { this.a = a; } var a = { name: 'qiu', birth: new Date(), pattern: /qiu/gim, container: document.body, hobbys: ['book', new Date(), /aaa/gim, 111] }; var c = new A(); var b = deepClone(c); console.log(c.a === b.a); console.log(c, b);
讲解:
var stu1={ id:10000, name:"张三", class:{ name:"WEB211001", // 数量==count count:40, } } // 直接赋值 对象的完全引用 相当于stu1和stu2 数值一样 // stu1.class.name="3"; // 引用关系 stu1和stu2的关系是引用类型 // var stu2 = stu1; // console.log(stu2); // 浅拷贝 把stu1的值给stu2这个对象 // 浅拷贝 缺点:非引用类型、指向 // 深拷贝 处理的是判断引用类型、不同的、包含数组、对象 var stu2 = {}; stu2.id = stu1.id; stu2.name = stu1.name; stu2.class = {}; stu2.class.name = stu1.class.name; stu2.class.count = stu1.class.count; console.log(stu2,stu1);