首页
留言
关于
友链
更多
足迹
Search
1
SpringMVC+Spring+MyBatis整合完整版Web实例(附数据)
2,828 阅读
2
关于在Flutter实现Google地图的方法
1,626 阅读
3
druid报异常 “sql injection violation, part alway true condition not allow”的解决方案
1,194 阅读
4
MyBatis的TooManyResultsException异常的解决办法
999 阅读
5
如何去掉vue路径中的“#”号
980 阅读
发现
技术
生活
户外
登录
Search
标签搜索
Git
JavaScript
Oracle
Git学习
Java
Flutter
MySQL
SQL Server
IntelliJ IDEA
Spring Boot
Flutter 2.0
对称加密算法
Google地图
Maven
ES6
秦岭户外
linux
Tomcat
Redis
Spring
Bai Keyang
累计撰写
282
篇文章
累计收到
277
条评论
首页
栏目
发现
技术
生活
户外
页面
留言
关于
友链
足迹
搜索到
11
篇与
JavaScript
的结果
2022-02-04
ES6之变量的解构赋值(Destructuring)
ES6中解构赋值主要分为6类,分别为 数组解构赋值 、对象解构赋值 、字符串解构赋值 、数值和布尔值解构赋值 、函数参数解构赋值 。什么是解构?在ES6中允许按照一定的模式从数组和对象中提取值,然后对变量进行赋值,这被称为解构(Destructuring)数组解构赋值数组的解构赋值时,等号的右边必须是数组,否则将会报错。只要数据结构具有Iterator借口,则都可以采用数组形式的解构赋值。let [a, b, c] = [1, 2, 3]; a // 1 b // 2 c // 3这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。下面是一些常见的解构例子:let [f, [[k], y]] = [1, [[2], 3]]; f // 1 k // 2 y // 3 let [ , ,t] = ["php", "java", "go"]; t // go let [x, ,y] = ["php", "java", "go"]; x // php y // go let [k, ...t] = [1, 2, 3, 4]; k // 1 t // [2, 3, 4] let [x, y, ...z] = ["php"]; x // php y // undefined z // []当解构不成功,变量的值就等于undefined。下面代码中,f均解构不成功,为undefined。let [f] = []; let [k, f] = [1];Set结构也可以使用数组的解构赋值,如下:let [x, y, z] = new Set(["php", "java", "go"]); x // php y // java z // go还有一种情况是部分解构成功,如下:let [x, y] = [1, 2, 3]; x // 1 y // 2 let [a, [b], c] = [1, [2, 3], 4] a // 1 b // 2 c // 4以上情况是不完全解构,即等号左边的模式只能匹配一部分等号右边的数组。但是这种情况下,结构依然可以成功。let [f] = 1; let [f] = false; let [f] = NaN; let [f] = undefined; let [f] = null; let [f] = {};以上语句都会报错,因为等号右边的值或是转为对象以后不具备iterator接口(前五个表达式),或是本事就不具备Iterator接口(最后一个表达式)数组解构赋值中允许指定 默认值 ,如:let [f = true] = []; f // true let [a, b = 'x'] = ['y']; a // y b // x let [a, b = 'c'] = ['k', undefined]; a // k b // c let [x = 1] = [undefined]; x // 1 let [x = 1] = [null]; x // null let [x = 1, y = x] = []; // x = 1, y = 1 let [x = 1, y = x] = [2]; // x = 2, y = 2 let [x = 1, y = x] = [1, 2]; // x = 1, y = 2 let [x = y, y = 1] = []; // 报错:ReferenceError最后一个表达式之所以报错,是因为x引用了默认值y时,y还没有被声明。注:ES6内部使用严格相等运算符(===)判断一个位置是否有值。如果一个数组成员不严格等于undefined,默认值是无法生效的。对象解构赋值对象的解构与数组有一个重要的不同:数组的元素是按次序排列的,变量的取值是由它的位置决定的;而对象的属性没有次序,变量必须与属性同名才能取到正确的值。示例如下:let {f, b} = {f: "AA", b: "KK"}; // f --> AA b --> KK let {c} = {f: "AA", b: "KK"}; // c --> undefined如果变量名与属性名不一致,必须用如下的形式:let {f: b} = {f: 'AA', b: 'CC'}; // b --> AA let obj = {hello: 'AA', world: 'CC'}; let {hello: h, world: w} = obj; // h --> hello w --> world hello --> error: hello is not defined对象的解构赋值的内部机制是先找到同名属性,然后再赋值给对应的变量。真正被赋值的是后者,而不是前者。对象解构也可以指定默认值:let {x = 3} = {}; // x --> 3 let {x, y = 5} = {x: 1}; // x --> 1 y --> 5 let {x : y = 3} = {}; // y --> 3 let {x : y = 3} = {y : 5}; // y --> 5 let {x = 3} = {x : undefined}; // x --> 3 let {x = 3} = {x : null}; // x --> null默认值生效的条件是对象的属性值严格等于undefined如果解构模式是嵌套的对象,而且子对象所在的父属性不存在,那么将会报错,如下:let {f: {bo}} = {bc: "bucc"}以上代码报错的原因是因为f此时等于undefined,再取子属性就会报错。如果将一个已经声明的变量用于解构赋值,必须非常小心。 常见的错误如下:// 错误的写法 let x; {x} = {x : 1}; // SyntaxError: syntax error // 正确的写法 let x; ({x} = {x : 1});以上代码中第一部分会报错,是因为JavaScript引擎会将{ x }理解成一个代码块,从而发生语法错误。只有不将大括号写在行首,避免JavaScript将其解释为代码块,才能解决这个问题。解构赋值允许等号左边的模式之中不放置任何变量名。虽然此类表达式毫无意义,但是语法合法,且可以正常执行:({} = [true, false]); ({} = 'abc'); ({} = []);由于数组本质是特殊的对象,因为可以对数组进行对象属性的解构:let arr = [1, 2, 3]; let {0: first, [arr.length - 1]: last} = arr; // first --> 1 last --> 3字符串解构赋值字符串在进行解构赋值时,字符串被转换成了一个类似数组的对象:let [a, b, c, d, e] = 'hello'; // a -->h a -->e a -->l a -->l a -->o类似数组的对象都有一个length的属性,因为还可以对这个属性进行解构赋值:let {length : len} = 'hello'; // len --> 5数值和布尔值解构赋值在数值和布尔值解构赋值时,如果等号右边是数值或布尔值,则会先转为对象。let {toString:s} = 123; s === Number.prototype.toString; // true let {toString:s} = true; s === Boolean.prototype.toString; // true解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。let {prop: x} = undefined; // TypeError let {prop: y} = null; // TypeError函数参数解构赋值函数的参数也可以使用解构赋值。function add([x, y]) { return x + y; } add([3, 2]); // 5 [[1,2],[3,4]].map(([a, b]) => a + b); // [3, 7]函数参数的解构也可以使用默认值:// 示例一 function move ({x = 0, y = 0} = {}) { return [x, y] } move({x: 3, y: 9}); // [3, 9] move({x: 3}); // [3, 0] move({}); // [0, 0] move(); // [0, 0] // 示例二 function move({x, y} = {x : 0, y : 0}) { return [x, y]; } move({x: 3, y: 9}); // [3, 9] move({x: 3}); // [3, undefined] move({}); // [undefined, undefined] move(); // [0, 0]关于圆括号的问题解构赋值虽然很方便,但是解析起来并不容易。对于编译器来说,一个式子到底是模式还是表达式,没有办法从一开始就知道,必须解析到(或解析不到)等号才能知道。ES6规则是只要有可能导致解构的歧义,就不得使用圆括号。但是这个在实际中并不那么容易判断,且处理起来相当的麻烦。所以也建议,只要有可能就不要在模式中放置圆括号。不能使用圆括号的情况情况一:变量声明语句let [(a)] = [1]; // 错误 let {x : (c)} = {};// 错误 let ({x : c}) = [1];// 错误 let {(x : c)} = [1];// 错误 let {(x) : c} = [1];// 错误 let {o : ({p:p})} = {o: {p: 2}};// 错误类似以上代码语句都会报错,因为它们都是变量声明语句,模式不能使用圆括号。情况二:函数参数函数参数也属于变量声明,因此不能使用圆括号function f ([(z)]) { return z; }// 错误 function f ([z, (x)]) {return x; }// 错误情况三:赋值语句的模式// 将整个模式放入圆括号之中导致报错 ({p : a}) = {p: 4};// 错误 ([a]) = [5];// 错误 // 将一部分模式放入圆括号之中导致报错 [({p: a}), {x: c}] = [{}, {}];// 错误可以使用圆括号的情况可以使用圆括号的情况只有一种:赋值语句的非模式部分可以使用圆括号。[(b)] = [3]; // 正确 ({p: (d)} = {});// 正确 [(parseInt.prop)] = [3];// 正确以上三个语句均执行正确,因为他们的都是赋值语句,而不是声明的语句,另外它们的圆括号都不属于模式的一部分。用途变量的解构赋值用途很多:交换变量的值let x = 1; let y = 2; [x, y] = [y, x];从函数返回多个值函数只能返回一个值,如果要返回多个值,只能将它们放在数组或对象里面返回。// 返回一个数组 function test1() { return [1, 2, 3]; } let [a, b, c] = test1(); // 返回一个对象 function test2() { return { a: 2, b: 6 }; } let {a, b} = test2();函数参数的定义解构赋值可以方便的将一组参数与变量名对应起来// 参数是一组有序的值 function f1([x, y, z]) { ... } f1([1, 2, 3]); // 参数是一组无序的值 function f2({x, y, z}}) { ... } f2({z: 8, x: 6, y: 4});提取JSON数据let jsonData = { id: 2, name: 'LiMing', data: [867, 22] } let {id, name, data: nums} = jsonData; console.log(id, name, nums); // 2, "LiMing", [867, 22]函数参数的默认值jQuery.ajax = function (url, { async = true, cache = true, complate = function(){} }) { // do stuff } 遍历Map结构任何部署了Iterator接口的对象都可以用for...of 循环遍历。Map结构原生支持Iterator接口,配合变量的解构赋值获取键名和键值就非常方便。let map = new Map(); map.set('one', 'hello'); map.set('two', 'world'); for(let [key , value] of map) { console.log(key, value); } // 获取键名 for(let [key] of map) { console.log(key); } // 获取键值 for(let [, value] of map) { console.log(value); }输入模块的指定方法:加载模块时往往需要指定输入的方法。解构赋值使得输入语句非常清晰:const {SourceMapConsumer, SourceNode} = require("source-map");
2022年02月04日
171 阅读
0 评论
0 点赞
2021-03-09
ES6标准入门之const命令
const声明一个只读的常量,一旦声明,常量的值就不能改变const P = 90; console.log(P); P = 88; // Uncaught TypeError: Assignment to constant variable.const声明的变量值不可改变,就意味着const一旦声明就必须立即初始化:const P; // Uncaught SyntaxError: Missing initializer in const declarationconst只声明不赋值就会报错const和let的作用域相同,只在声明所在的块级作用域内有效:if (true) { const P = 5; } P // Uncaught ReferenceError: P is not definedconst声明的常量同样存在暂时性死区,只能在声明的位置后使用:if (true) { console.log(P); // ReferenceError const P = 5; }P声明之前就调用,结果报错const声明的常量,与let一样,不可重复声明:var message = "Hello!"; let age = 25; // 以下两行都会报错 const message = "Goodbye!"; const age = 30;理解:const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。因此,将一个对象声明为常量必须非常小心。常量foo储存的是一个地址,这个地址指向一个对象。不可变的只是这个地址,即不能把foo指向另一个地址,但对象本身是可变的,所以依然可以为其添加新属性:const foo = {}; // 为 foo 添加一个属性,可以成功 foo.prop = 123; foo.prop // 123 // 将 foo 指向另一个对象,就会报错 foo = {}; // TypeError: "foo" is read-only常量a是一个数组,这个数组本身是可写的,但是如果将另一个数组赋值给a,就会报错:const a = []; a.push('Hello'); // 可执行 a.length = 0; // 可执行 a = ['Dave']; // 报错使用Object.freeze方法将常量foo指向的一个对象冻结,所以添加新属性不起作用,严格模式时还会报错。const foo = Object.freeze({}); // 常规模式时,下面一行不起作用; // 严格模式时,该行会报错 foo.prop = 123;除了将对象本身冻结,对象的属性也应该冻结。下面是一个将对象彻底冻结的函数var constantize = (obj) => { Object.freeze(obj); Object.keys(obj).forEach( (key, i) => { if ( typeof obj[key] === 'object' ) { constantize( obj[key] ); } }); };扩展:ES6 声明变量的六种方法:ES5 只有两种声明变量的方法:var命令和function命令。ES6 除了添加let和const命令,后面章节还会提到,另外两种声明变量的方法:import命令和class命令。所以,ES6 一共有 6 种声明变量的方法。
2021年03月09日
421 阅读
0 评论
0 点赞
2021-02-23
JavaScript 总结归纳图
javascript DOM操作:javascript windows对象:javascript变量:javascript函数基础:javascript流程控制:javascript数据类型:javascript数组:javascript运算符:javascript正则表达式:javascript字符串函数:
2021年02月23日
337 阅读
0 评论
0 点赞
2018-08-04
JS获取设备分辨率、宽、高
JS获取显示设备的分辨率:屏幕分辨率的宽:window.screen.width 屏幕分辨率的高:window.screen.heightJS获取显示设备可用工作区信息: 屏幕可用工作区宽度:window.screen.availWidth 屏幕可用工作区高度:window.screen.availHeightJS获取显示设备其他信息: 你的屏幕设置颜色位数:window.screen.colorDepth 位彩色 你的屏幕设置 像素/英寸: window.screen.deviceXDPI 像素/英寸 JS获取浏览器的显示相关信息: 网页可见区域宽:document.body.clientWidth 网页可见区域高:document.body.clientHeight 网页可见区域宽:document.body.offsetWidth (包括边线和滚动条的宽) 网页可见区域高:document.body.offsetHeight (包括边线的宽) 网页正文全文宽:document.body.scrollWidth 网页正文全文高:document.body.scrollHeight 网页被卷去的上:document.body.scrollTop 网页被卷去的左:document.body.scrollLeft 网页正文部分上:window.screenTop 网页正文部分左:window.screenLeft JQuery 获取浏览器的显示相关信息: $("body").width():width()返回元素的宽高,不包括padding/border/margin $("body").innerWidth():innerWidth()返回元素的宽高 + padding $("body").outerWidth():outerWidth()返回元素的宽高 + padding + border $("body").outerWidth(true):outerWidth(true)返回元素宽高 + padding + border + margin $(window).height():浏览器当前窗口可视区域高度 $(document).height():浏览器当前窗口文档的高度 $(document.body).height():浏览器当前窗口文档body的高度 $(document.body).outerHeight(true):浏览器当前窗口文档body的总高度 包括border padding margin $(window).width():浏览器当前窗口可视区域宽度 $(document).width():浏览器当前窗口文档对象宽度 $(document.body).width():浏览器当前窗口文档body的高度 $(document.body).outerWidth(true:浏览器当前窗口文档body的总宽度 包括border padding margin
2018年08月04日
269 阅读
0 评论
0 点赞
2018-05-30
ES6标准入门(二)块级作用域
在ES5中只有全局的作用域和函数作用域,没有块级作用域。这导致在很多场合不合理。如:var temp = new Date(); function go () { console.log(temp); if(false){ var temp = 'hello world'; } } go(); // undefined上面代码的本意是,if代码块的外部使用外层的temp变量,内部使用内层的temp变量。但是go函数执行后,输出的结果是undefined,原因在于变量提升导致内层的temp变量覆盖了外层的temp变量。还有一种情况,用来计数的循环变量泄露为全局变量。如:var s = 'hello'; for(var i = 0; i < s.length; i++){ console.log(s[i]); } console.log(i);//5上面代码中,变量i只用来控制循环,但是循环结束后i并没有消失,而是泄露成了全局变量。ES6块级作用域 let实际上为Javascript新增了块级作用域。function f1 () { let n = 6; if(true){ let n = 10; } console.log(n); //6 }上面代码的函数有两个代码块都声明了变量n,运行后结果输出5。这就表明外层代码块不受内层代码块的影响。如果使用var定义变量的n话,最后输出的值就是10了。 在ES6中允许块级作用域的任意嵌套。如:{{{{ let text = 'Hello World'; }}}};上面代码嵌套了4层的块级作用域。外层作用域无法读取内层作用域的变量。如:{{{{ {let text = 'Hello World';} console.log(text);// 报错 }}}};内层作用域可以定义外层作用域的同名变量。{{{{ let text = 'Hello World'; {let text = 'Hello World';} }}}};块级作用域的出现,实际上使得获得广泛应用的立即执行匿名函数(IIFE)不再必要了。// IIFE写法 (function (){ var temp = 'hello'; ... }()); // 块级作用域写法 { let temp = 'hello'; ... }块级作用域的函数声明 在ES5中规定,函数只能在顶层作用域和函数作用域中声明,不能在块级作用域中声明。如:// 情况一 if(true){ function f() {} } // 情况二 try{ function f() {} }catch(e){ //... }上面这两种函数声明在ES5中都是非法的。但是实际情况是,以上两种情况都能运行,并不会报错。原因是,浏览器没有遵守这个规定,浏览器为了兼容以前的旧代码,还是支持在块级作用域中声明函数。 在ES6中引入了块级作用域,明确允许在块级作用域中声明函数。ES6规定,在块级作用域中函数声明语句的行为类似于let,在块级作用域之外不可应用。如:function f() {console.log('我在外面');} (function(){ if(false){ function f() {console.log('我在里面');} } f(); }());上面代码在ES5中的运行结果是 “我在里面”,因为在if内声明的函数f会被提升到函数头部,实际代码运行如下:// ES5环境 function f() {console.log('我在外面');} (function(){ function f() {console.log('我在里面');} if(false){ } f(); }());而在ES6中运行就完全不一样了,理论上会得到“我在外面”。因为在块级作用域内声明的函数类似于let,对作用域之外的没有影响。 但是真正在ES6浏览器中运行却会报错。这是不是很奇怪? 由于如果改变了块级作用域内声明的函数的处理规则,显然势必会对旧的代码产生非常大的影响。为了减轻因此产生的不兼容问题,ES6在附录B(http://www.ecma-international.org/ecma-262/6.0/index.html#sec-block-level-function-declarations-web-legacy-compatibility-semantics)中规定,浏览器的实现可以不遵守上面的规定,可以有自己的行为方式(https://stackoverflow.com/questions/31419897/what-are-the-precise-semantics-of-block-level-functions-in-es6),具体如下:1、允许在块级作用域内声明函数。2、函数声明类似于var,即会提升到全局作用域或函数作用域的头部。3、同时,函数声明还会提升到所在的块级作用域的头部。需要特别注意,上面3条规则只对 ES6 的浏览器实现有效,其他环境的实现不用遵守,还是将块级作用域的函数声明当作let处理。根据这3条规则,在浏览器的 ES6 环境中,块级作用域内声明的函数,行为类似于var声明的变量。如:// 浏览器的 ES6 环境 function f() { console.log('我在外面'); } (function () { if (false) { function f() { console.log('我在里面'); } } f(); }()); // Uncaught TypeError: f is not a function尽管如此,上面的代码在符合ES6的浏览器中都会报错,因为实际运行的下面的代码:// 浏览器的 ES6 环境 function f() { console.log('我在外面'); } (function () { var f = undefined; if (false) { function f() { console.log('我在里面'); } } f(); }()); // Uncaught TypeError: f is not a function所以,考虑到环境导致的行为差异太大,我们应该避免在块级作用域中声明函数。如果确实需要,也应该写成函数表达式的形式,而不是函数声明语句。如:// 函数声明语句 { let a = 'secret'; function f() { return a; } } // 函数表达式 { let a = 'secret'; let f = function () { return a; }; }另外,还有一个需要注意的地方。在ES6的块级作用域允许声明函数的规则只在使用大括号的情况下才成立,如果没有使用大括号,就会报错。如:// 不报错 'use strict'; if (true) { function f() {} } // 报错 'use strict'; if (true) function f() {} do表达式 本质上,块级作用域是一个语句,将多个操作封装在一起,没有返回值。如:{ let t = f(); t = t * t + 1; }在ES6中有一个提案(http://wiki.ecmascript.org/doku.php?id=strawman:do_expressions),使得块级作用域可以变为表达式,即可以返回值,办法就是在块级作用域之前加上do,使它变为do表达式。如:let x = do { let t = f(); t * t + 1; }上面的代码中,变量x会得到整个块级作用域的返回值。
2018年05月30日
261 阅读
0 评论
0 点赞
2018-05-22
ES6标准入门(一)let命令
let命令,用于声明变量,用法和var类似,但是所声明的变量只在let命令所在的代码块内有效。{ let a = 1; var b = 2; } console.log(a);// 报错:ReferenceError: a is not defind. console.log(b);// 2在代码块外面调用了let命令声明的a就会报错,而调用var声明的b返回了正常的值。这就说明let声明的变量只在其所在的代码块中有效。下面是一个关于let和var在for循环中的例子:使用var声明变量的for循序特殊例子:var a = []; for(var i = 0; i < 10; i++){ a[i] = function(){ console.log( i ); } } a[6] (); // 10 变量i是var声明的,在全局范围内有效,所以全局只有一个变量,每一次循环i的值都会发生改变,被赋值给数组a的函数内部的console.log(i) 的i指向的是全局的i。也就是说,所有数组a的成员中i都指向的是同一个i,导致输出时输出的是最后一轮的i,也就是10。使用let声明变量的for循序特殊例子:var a = []; for(let i = 0; i < 10; i++){ a[i] = function(){ console.log( i ); } } a[6] (); // 6 变量i是let声明的,i只在本轮循环内有效,所以每一次循环的i其实都是一个新的变量。这是因为在Javascript引擎内部会记住上一轮的循环的值,初始化本轮的变量i时,就会在上一轮循环的基础上进行计算。for循环还有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。for(let i = 0; i <i 10; i++){ let i = 'abc'; console.log( i ); } // abc // abc // abc ……上面的结果就是连续输出10次的'abc'。这就表明函数内部的变量i与循环变量i不再同一个作用域。let不存在变量提升的现象。使用var命令声明的变量会发生 “变量提升”现象,就是变量可以在声明之前使用,值为undefined。按照一般的逻辑,变量应该是在声明之后才能被使用的。当然,在ES6中,let命令改变了语法行为,它所声明的变量一定要在声明后使用。如:// var的情况 console.log(foo);// 输出undefined var foo = 2; // let的情况 console.log(fo);// 报错 ReferenceError let fo = 2; let暂时性死区只要在块级作用域存在let命令,则它所声明的变量就 “绑定”(binding) 这个区域,不再受外部的影响。如:var temp = 123; if(true){ temp = 'abc'; // ReferenceError let temp; }上面代码中存在全局变量temp,但是在块级作用域内let又声明了一个局部变量temp,导致后者绑定这个块级作用域,所以在let声明变量之前对temp赋值会报错。在ES6中,如果区域块中存在let和const命令,则这个区域块对这些命令声明的变量从一开始就会形成封闭作用域。只要是在声明之前使用这些变量,则就会报错。所以在代码块内,使用let命令声明变量之前,该变量都是不可用的,这在语法上称为“暂时性死区”(temporal dead zone,简称TDZ)。如:if(true){ // TDZ 开始 temp = 'abc'; // ReferenceError console.log(temp); //ReferenceError let temp; // TDZ 结束 console.log(temp); // undefined temp = 123; console.log(temp); // 123 }上面代码中,在let命令声明变量temp之前都是变量temp的“死区”。这里要提到的一点是,“暂时性死区”也意味着typeof不再是一个百分百安全的操作了。typeof a;// ReferenceError let a;上面代码变量a使用let声明,在声明之前都属于a的“死区”,只要该变量被使用就会报错。但是,如果一个变量根本就没有被声明,使用typeof反而不会报错。typeof okey; // undefined上面代码中okey是一个不存在的变量,所以结果返回undefined。在没有let之前,typeof运算符是百分之百的安全,永远不会报错。这样的设计初衷也是为了让大家养成良好的编程习惯,变量一定要在声明之后使用,否则就会报错。在我们平时的实际使用中,还有一些比较隐蔽的“死区”,这样的“死区”不太容易被发现。如:function go (x = y, y = 6) { return [x, y]; } go(); // 报错上面代码中,调用go函数之所以报错是因为参数x值等于另外一个参数y,而此时y还没有被声明,属于“死区”。反过来如果y的默认值是x的话,则就不会报错,因为此时x已经被声明了。如:function go (x = 6, y = x) { return [x, y]; } go(); // [6, 6]另外,下面的代码也会报错。如:// 不报错 var x = x; // 报错 let y = y; // ReferenceError: y is not defined上面代码报错也是暂时性死区。在使用let声明变量时,只要变量在还没有声明前使用就会报错。ES6规定暂时性死区和let、const语句不出现变量提升,主要是为了减少运行时错误,防止在变量声明前就使用这个变量,从而导致意料之外的行为。暂时性死区的本质就是,只要进入当前作用域,所要使用的变量就已经存在,但是不可获取,只有等到声明变量的那一行代码出现后,才可以获取和使用该变量。let不允许重复声明let不允许在相同的作用域内重复声明同一个变量。如:// 报错 function () { let a = 1; var a = 10; } // 报错 function () { let a = 1; let a = 10; } function func(arg) { let arg;// 报错 } function (arg) { { let arg; // 不报错 } }
2018年05月22日
239 阅读
0 评论
0 点赞
2018-05-21
对Javascript中Date对象的扩展
对Javascript中Date日期的扩展,主要是对Date对象扩展了一个格式化日期和解析日期的方法。/** * 日期解析器 * @param value * @param format * @return {Date} */ Date.prototype.parser = function(value,format) { var shortMonthsToNumber = []; shortMonthsToNumber["Jan"] = "01"; shortMonthsToNumber["Feb"] = "02"; shortMonthsToNumber["Mar"] = "03"; shortMonthsToNumber["Apr"] = "04"; shortMonthsToNumber["May"] = "05"; shortMonthsToNumber["Jun"] = "06"; shortMonthsToNumber["Jul"] = "07"; shortMonthsToNumber["Aug"] = "08"; shortMonthsToNumber["Sep"] = "09"; shortMonthsToNumber["Oct"] = "10"; shortMonthsToNumber["Nov"] = "11"; shortMonthsToNumber["Dec"] = "12"; var parseMonth = function (value) { return shortMonthsToNumber[value] || value; }; var padding = function(value, length) { var paddingCount = length - String(value).length; for(var i = 0 ; i < paddingCount ; i++) { value = "0" + value; } return value; }; var parseTime = function (value) { var retValue = value; var millis = ""; if (retValue.indexOf(".") !== -1) { var delimited = retValue.split('.'); retValue = delimited[0]; millis = delimited[1]; } var values3 = retValue.split(":"); if (values3.length > 0) { var hour = 0,minute = 0,second = 0,millis = 0; var hour = values3[0]; if(values3[1]){ minute = values3[1]; } if(values3[2]){ second = values3[2]; } return { time: retValue, hour: hour, minute: minute, second: second, millis: millis }; } else { return { time: "", hour: "", minute: "", second: "", millis: "" }; } }; var resultDate = new Date(1970, 01, 01, 00, 00, 00); if(!value){ return resultDate; } try { var date = null; var year = null; var month = null; var dayOfMonth = null; var time = null; if (typeof value == "number"){ return this.date(new Date(value), format); } else if (typeof value.getFullYear == "function") { year = value.getFullYear(); month = value.getMonth() + 1; dayOfMonth = value.getDate(); time = parseTime(value.toTimeString()); } else if (value.search(/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.?\d{0,3}[Z\-+]?(\d{2}:?\d{2})?/) != -1) { /* 2009-04-19T16:11:05+02:00 || 2009-04-19T16:11:05Z */ var values = value.split(/[T\+-]/); year = values[0]; month = values[1]; dayOfMonth = values[2]; time = parseTime(values[3].split(".")[0]); date = new Date(year, month - 1, dayOfMonth); } else { var values = value.split(" "); switch (values.length) { case 6: /* Wed Jan 13 10:43:41 CET 2010 */ year = values[5]; month = parseMonth(values[1]); dayOfMonth = values[2]; time = parseTime(values[3]); date = new Date(year, month - 1, dayOfMonth); break; case 2: /* 2009-12-18 10:54:50.546 */ var values2 = values[0].split("-"); year = values2[0]; month = values2[1]; dayOfMonth = values2[2]; time = parseTime(values[1]); date = new Date(year, month - 1, dayOfMonth); break; case 7: /* Tue Mar 01 2011 12:01:42 GMT-0800 (PST) */ case 9: /* Fri Apr 08 2011 00:00:00 GMT+0800 (China Standard Time) */ case 10: /* Fri Apr 08 2011 00:00:00 GMT+0200 (W. Europe Daylight Time) */ year = values[3]; month = parseMonth(values[1]); dayOfMonth = values[2]; time = parseTime(values[4]); date = new Date(year, month - 1, dayOfMonth); break; case 1: /* 2012-02-07CET00:00:00 */ var values2 = values[0].split(""); if(values2.length > 3){ year=values2[0]+values2[1]+values2[2]+values2[3]; } if(values2.length > 6){ month= values2[5]+values2[6]; } if(values2.length > 9){ dayOfMonth = values2[8]+values2[9]; } if(values2.length > 20){ time = parseTime(values2[13]+values2[14]+values2[15]+values2[16]+values2[17]+values2[18]+values2[19]+values2[20]); } if(Number(dayOfMonth) && Number(month) && Number(dayOfMonth)){ date = new Date(year, month - 1, dayOfMonth); }else { return new Date(); } break; default: return new Date(); } } var pattern = ""; if(!format){ format = "yyyy-MM-dd HH:mm:ss"; } for (var i = 0; i < format.length; i++) { var currentPattern = format.charAt(i); pattern += currentPattern; switch (pattern) { case "D": case "ddd": pattern = ""; break; case "dd"://月中的某一天。一位数的日期有一个前导零。 if (format.charAt(i + 1) == "d") { break; } resultDate.setDate(Number(padding(dayOfMonth, 2))); pattern = ""; break; case "d"://月中的某一天。一位数的日期没有前导零。 if (format.charAt(i + 1) == "d") { break; } resultDate.setDate(parseInt(dayOfMonth, 10)); pattern = ""; break; case "MMMM"://月份的完整名称,在 MonthNames 中定义。 case "MMM"://月份的缩写名称,在 AbbreviatedMonthNames 中定义。 pattern = ""; break; case "MM"://月份数字。一位数的月份有一个前导零。 if (format.charAt(i + 1) == "M") { break; } resultDate.setMonth(Number(padding(month, 2))-1, 1); pattern = ""; break; case "M"://月份数字。一位数的月份没有前导零。 if (format.charAt(i + 1) == "M") { break; } resultDate.setMonth(parseInt(month, 10)-1, 1); pattern = ""; break; case "y"://不包含纪元的年份。不具有前导零。 case "yyy": if (format.charAt(i + 1) == "y") { break; } pattern = ""; break; case "yy"://不包含纪元的年份。具有前导零。 if (format.charAt(i + 1) == "y" && format.charAt(i + 2) == "y") { break; } resultDate.setFullYear(Number('19'+String(year).slice(-2)), 1, 1); pattern = ""; break; case "yyyy"://包括纪元的四位数的年份。 resultDate.setFullYear(Number(year), 1, 1); pattern = ""; break; case "HH"://24 小时制的小时。一位数的小时数有前导零。 resultDate.setHours(Number(padding(time.hour, 2))); pattern = ""; break; case "H"://24 小时制的小时。一位数的小时数没有前导零。 if (format.charAt(i + 1) == "H") { break; } resultDate.setHours(parseInt(time.hour, 10)); pattern = ""; break; case "hh"://12 小时制的小时。一位数的小时数有前导零。 var hour = (time.hour == 0 ? 12 : time.hour < 13 ? time.hour : time.hour - 12); resultDate.setHours(Number(padding(hour, 2))); pattern = ""; break; case "h"://12 小时制的小时。一位数的小时数没有前导零。 if (format.charAt(i + 1) == "h") { break; } var hour = (time.hour == 0 ? 12 : time.hour < 13 ? time.hour : time.hour - 12); resultDate.setHours(parseInt(hour, 10)); pattern = ""; break; case "mm"://将分钟显示为带前导零的数字 resultDate.setMinutes(Number(padding(time.minute,2))); pattern = ""; break; case "m"://将分钟显示为不带前导零的数字 if (format.charAt(i + 1) == "m") { break; } resultDate.setMinutes(Number(time.minute)); pattern = ""; break; case "ss"://将秒显示为带前导零的数字 resultDate.setSeconds(Number(padding(time.second.substring(0, 2), 2))); pattern = ""; break; case "s"://将秒显示为不带前导零的数字 if (format.charAt(i + 1) == "s") { break; } resultDate.setSeconds(Number(time.second)); pattern = ""; break; default: pattern = ""; break; } } return resultDate; } catch(e) { if(window.console){ console.error(e); } return resultDate; } }
2018年05月21日
234 阅读
0 评论
0 点赞
2018-05-16
[方案]JS模拟表单方式下载文件
近期在调整代码的时候,遇到了一个小问题:在程序里面需要下载文件,最开始的采取的做法是window.open和window.location.href这两种形式请求后台下载文件的,贴上代码:前台关键代码:function downloadFile(url){ if($.browser.msie) { // IE下打开空下载页面 window.open(glo.path + url , "_blank" , "width=500,height=400") ; }else{ window.location.href = url.replace(/\|/g, "&"); } }后台关键代码:@RequestMapping("/downloadFile") public void download(HttpServletRequest request, HttpServletResponse response) { logger.info("ajax download file"); response.setCharacterEncoding("utf-8"); response.setContentType("multipart/form-data");//application/octet-stream InputStream inputStream = null; OutputStream os = null; try { String path = PropertiesUtil.getValue("filedisk");//filedisk D:\upload_file\demo\ String downFile = "2018\02\10\test.pdf"; String fileNameStr = downFile.substring(downFile.lastIndexOf("\\"), downFile.lastIndexOf(".")); fileNameStr = fileNameStr.replaceAll(" ", ""); String exp = downFile.substring(downFile.lastIndexOf("."),downFile.length()); String fileName = fileNameStr.substring(1, fileNameStr.length())+exp; response.setHeader("Content-Disposition", "attachment;fileName="+ java.net.URLEncoder.encode(fileName, "UTF-8")); File file = new File(path+ downFile); if (file != null && file.exists()) { inputStream = new FileInputStream(new File(path+ downFile)); os = response.getOutputStream(); byte[] b = new byte[2048]; int length; while ((length = inputStream.read(b)) > 0) { os.write(b, 0, length); } } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally { // 这里主要关闭。 try { if(null != os){ os.close(); } } catch (IOException e) { } try { if(null != inputStream){ inputStream.close(); } } catch (IOException e) { } } }上面这代码的最大问题就是,当下载文件不存在时就会出现页面加载失败的情况。采用window.open这种方式就会打开一个新的窗口页面出现请求失败;采用window.location.href这种形式就会在当前页面直接跳转,然后显示页面加载失败的情况。然后着手修改,模拟表单提交后台的形式,前端代码修改如下:function downloadFile(url){ var form = $("<form></form>").attr("action", url).attr("method", "post"); // 隐藏域传递参数 form.append($("<input></input>").attr("type", "hidden").attr("name", "downFileName").attr("value", 'Demo.txt')); form.appendTo('body').submit().remove(); }上面就是将前台的下载方法通过模拟表单提交的方式实现post传递数据。OK,到这里,开头的那个问题,解决,收工。
2018年05月16日
286 阅读
0 评论
0 点赞
2017-12-27
JavaScript将数组字符串转数组对象的方法
在JavaScript,如何将数组字符串转数组对象呢?可以利用强大的eval函数可以实现。如有一个数组字符串 [7,9,16,5] ,使用eval就可以将该字符串转换为数据。具体实现如下:var arrayText = "[7,9,16,5]"; console.log( arrayText );// 打印数组字符串 var array = eval( "(" + arrayText + ")" ) console.log( array );// 打印数组对象可以从控制台看出, [7,9,16,5]数组字符串已经被转换为一个数组对象,并具有了数组的特性,如果长度、方法等。
2017年12月27日
217 阅读
0 评论
0 点赞
2017-05-24
Javascript设计模式之Module(模块)模式
模块化是任何强大的应用程序架构中不可或缺的一部分,它通常能帮助我们清晰的分离和组织项目中的代码单元。在Javascript中,实现模块的方式包括:1、对象字面量表示法2、Module模式3、AMD模式4、CommonJS模式5、ECMAScript Harmony模式1、对象字面表示法:一个对象被描述为一组包含在大括号({})中,以逗号分割的键值(name/value) 对。对象名称可以是字符串或标识符,名称和值直接用冒号连接。对象中最后一个键值(name/value) 对后面不用加逗号,加逗号会导致报错。对象字面量不需要使用new运算符来进行实例化。下面是一个使用对象字面量表示法定义的例子:var myModule = { myProperty : "testValue", // 对象字面量可以包含属性和方法 myConfig : { useCaching : true, language : "en" }, // 基本方法 myMethod : function () { console.log("The name of this method is myMethod."); }, // 根据当前配置输出信息 myMethod2 : function () { console.log("Caching is:" + (this.myConfig.useCaching) ? "enabled" : "disabled"); }, // 重写当前配置 myMethod3 : function (newConfig) { if (typeof newConfig == "object") { this.myConfig = newConfig; console.log(this.myConfig.language); } }, }; myModule.myMethod(); myModule.myMethod2(); myModule.myMethod3({ language : "cn", useCaching : false });使用对象字面量有助于封装和组织代码。 2、Module(模块)模式Module模式最初被定义为一种在传统软件工程中为类提供私有和公有封装的方法。在Javascript中,Module模式用于进一步模拟类的概念,通过这种方式能够是一个单独的对象拥有公有/私有方法和变量,从而屏蔽来自全局作用域的特俗部分。降低函数名与在页面上其他脚本定义的函数冲突的可能性。Module模式使用闭包封装“私有”状态和组织,提供一种包装混合公有/私有方法和变量的方式,防止其泄露至全局作用域,与其他的开发人员的代码发生冲突。Module模式只提供一个公有的API,而其他的一切则都是维持在私有闭包里。Module模式提供了一个屏蔽处理底层事件逻辑的整洁解决方案,同时只暴露一个接口供应用程序的其他部分使用。Module模式非常类似于一个立即调用的函数表达式,它返回的是一个对象而不是一个函数。在Javascript中没有真正意义上的“私有”,它不像其他语言一样,它没有访问修饰符。从技术角度来讲,我们不能称变量为共有或私有,因此需要使用函数作用域来模拟这个概念。Module模式由于闭包的存在,声明的变量和方法只在该模式内部可用,但是它返回对象上定义的变量和方法对外部都是可以用的。下面是一个Module模式 的简单例子,包含命令空间、公有和私有变量:var myNamespace = (function () { // 私有变量 var myPrivateVar = 1; // 私有函数 var myPrivateMethod = function (text) { console.log(text); } return { // 公有变量 myPublicVar: "Hello JS", // 调用私用变量和私有方法的公有函数 myPublicMethod: function (name, age) { // 累加私有变量 myPrivateVar++; // 传入参数调用私有方法 myPrivateMethod("My name is " + name + "," + age + " years old this year"); } } })();3、Module模式引入混入Module模式允许将全局变量作为参数传递给模块的匿名函数,并按照我们所希望的为他们取个本地别名。// 全局模块 var myModule = (function (JQ, _) { function privateMethod1(){ JQ('.container').html('test'); } function privateMethod2(){ console.log(_.min([300,2,100,78,45])); } return { publicMethod: function(){ privateMethod1(); } } })(); myModule.publicMethod();4、工具包、框架的Module模式的实现关于工具包、框架的Module模式的实现,在这里就以我们常用的jQuery为例,定义library函数,声明一个新库,并在创建的新库时将init函数自动绑定到document.ready上:function library(module){ $(function(){ if(!module.init){ module.init(); } }); } var myLibrary = library(function(){ return { init: function(){ //模块实现 } }; })();总结一下Module模式的优缺点:优点:Module相比真正封装的思想,它对于很多拥有面向对象背景的开发人员来说更加整洁;它支持私有数据,在Module模式中,代码的公有(Public)部分能够接触私有部分,但是外界无法接触类的私有部分。缺点:由于我们访问公有和私有成员的方式不同,当我们想改变可见性时,实际上我们必须要修改每一个曾经使用该成员的地方;我们也无法访问那些之后在方法里添加的私有成员;无法为私有成员创建自动化单元测试,bug需要修补时会增加额外的复杂性,我们必须覆盖所有与有bug的私有方法进行交互的公有方法,开发人员也无法轻易的扩展私有方法,私有方法并不像最初显现出来的那么灵活。
2017年05月24日
282 阅读
0 评论
0 点赞
2014-07-20
JavaScript对字符串的截取
最近遇到的最多的应该就是JavaScript 方面的了。经常对页面上的一些值进行计算、拼接等操作,这些基本上我都是使用JavaScript 做的,同是也让我这个JavaScript 小白在这方面长进不少。截取字符串,详细大家都应该遇到过吧。比如,从后台返回很长的一段文字,如果全部放在页面,会将前台的样式打乱,这个时候,通常情况下会对字符串做一些处理(截取字符串)等。下来我就来介绍我常用的四种截取字符串的方法吧。如字符串:qwertyuiopfsd要求截取倒数两个字符,也就是“sd”。以下是几种实现方式:方法1:<script> string="qwertyuiopfsd" alert(string.substring(string.length-2,string.length)) </script>方法2:<script> alert("qwertyuiopfsd".match(/.*(.{2})/)[1]) </script>方法3:qwertyuiopfsd<script> alert("qwertyuiopfsd".match(/.{2}$/)) </script>方法4:<script> alert("qwertyuiopfsd".slice(-2)) </script>以上就是我较常用的四种截取字符串的方法,自己比较懒,喜欢使用第四种截取的方法。感觉使用的亲们可以收藏了哦。
2014年07月20日
296 阅读
0 评论
0 点赞