掌握-Javascript-类型转换隐式转换救救孩子

在上一篇中我们聊过了 JS 类型转换的规则和我发现的一些常见书籍中关于类型转换的一些小错误,当碰到显示类型转换的时候大家可以按照这些规则去拆解出答案。但 JS 中存在一些很隐晦的隐式类型转换,这一篇就来谈下我对隐式类型转换的一些总结。 关于 JS 类型转换规则请看上一篇的内容:掌握 JS 类型转换:从规则开始什么是隐式类型转换呢?顾名思义就是有时候你感觉不到这是类型转换但是实际上类型转换已经发生了。所以这个 "隐式" 取决于我们的理解和经验,如果你看不出来那就是隐式的。 下面按照我自己对于隐式转换的分类来逐个聊聊吧。 一元操作符 +、-var a = '123';var b = +a;console.log(b); // 123先来看看 + 或 - 在一个类型值前面,这里会执行 ToNumber 类型转换。如果是 - 在前面的话,还会将结果的符号取反,如:-"123" 的结果是 -123。并且如果原类型是对象的话也是遵循 ToNumber 的转换规则,大家可以自己试试,这里就不再举多余的例子了。 二元操作符接下来我们来看一下二元操作符相关的隐式转换,比如:+、-、&&、||、==等等这些。 相减 a - bvar a = '123';var b = true;console.log(a - b); // 122当执行减法操作时,两个值都会先执行 ToNumber 转换,所以这个是比较简单的,当类型是对象时也是遵循同样的规则。 相加 a + bconsole.log('123' + 4); // "1234"console.log(123 + true); // 124相加的情况有点复杂,但隐式转换的规则大家可以按照我总结的来记: 如果 + 的操作数中有对象,则执行 ToPrimitive 并且 hint 是 Number如果 + 中有一个操作数是字符串(或通过第一步得到字符串),则执行字符串拼接(另一个操作数执行 ToString 转换),否则执行 ToNumber 转换后相加这个相加操作的隐式转换规则看似有点麻烦,其实解析后还是很明确的。 ...

June 13, 2019 · 3 min · jiezi

解决vue项目在IE浏览器运行打开空白使用babelpolyfilll把ES6转ES5

IE的话一般需要将ES6转ES5。下面转换方法:需要安装babel-polyfill npm install --save babel-polyfill然后在main.js引入该插件 最后需要在webpack配置入口即可 app: ['babel-polyfill', './src/main.js'] 最后,重启一下项目 npm run dev

June 13, 2019 · 1 min · jiezi

es5中判断多个ajax全部执行完成后进行回调

$.when() jQuery中的api$.when($.ajax({ url: url, dataType: “jsonp”, data: {“movieId”: 248906}, success: function(data) { var data = data.data.data; var actorList = data.stars.split(’,’).join(’ | ‘); var type = data.category.split(’,’).join(’ | ‘); result.push({ “actorList”: actorList, “type”: type }) }, error: function(err) { console.log(err) }}), $.ajax({ url: url, dataType: “jsonp”, data: {“movieId”: 1212492}, success: function(data) { var data = data.data.data; var actorList = data.stars.split(’,’).join(’ | ‘); var type = data.category.split(’,’).join(’ | ‘); result.push({ “actorList”: actorList, “type”: type }) }, error: function(err) { console.log(err) }}), $.ajax({ url: url, dataType: “jsonp”, data: {“movieId”: 346765}, success: function(data) { var data = data.data.data; var actorList = data.stars.split(’,’).join(’ | ‘); var type = data.category.split(’,’).join(’ | ‘); result.push({ “actorList”: actorList, “type”: type }) }, error: function(err) { console.log(err) }})).done(function() { console.log(result)})全局变量判断var ajax_done_count = 0;function ajax_done() { if(ajax_done_count === 3) { // 渲染页面 } else { // 什么都不做 }}$.ajax({success: function(data) { ajax_done_count += 1; ajax_done();}})$.ajax({success: function(data) { ajax_done_count += 1; ajax_done();}})$.ajax({success: function(data) { ajax_done_count += 1; ajax_done();}})promise.all貌似在es5中也支持promise了 …我写了一个demovar url = ‘http://xxx/xxx’;var idList = [248906, 1212492, 346765, 341139];var result = [];var promiseArr = [];function createPromise (id) { return new Promise(function(resolve, reject) { $.ajax({ url: url, dataType: “jsonp”, data: {“movieId”: id}, success: function(data) { var data = data.data.data; resolve({ “actorList”: data.stars, “type”: data.category }); }, error: function(err) { reject(err); } }) }) }idList.map(function(i) { promiseArr.push(createPromise(i))})console.log(promiseArr)Promise.all(promiseArr).then(function(data) { result = data; console.log(result); // 渲染页面}).catch(function(err) { console.log(err);}) ...

April 15, 2019 · 2 min · jiezi

ES5的用对象实现类的作用

// js实现类 ES5// 由于函数始对象 所以写法不是唯一// function Pf(){}更符合固有思想// 由于ES6添加了类,所以书写变得跟方便var Pf = function(name,age){ // 私有属性方法外面不能调用,只有对象方法可以操作,从而起到保护数据的作用 // 私有属性 var secret = ‘小秘密’ // 私有方法 function secretfn(){ console.log(‘私有方法’) console.log(“my secret is”+secret) } // 公共属性方法是每创建一个对象就会创建一个该属性或方法(耗费一定把内存) // 共有实例属性 this.name = name this.age = age //共有实例方法 this.say = function(){ console.log(“my name is”+this.name,“my age is”+this.age) console.log(‘可以操作私有属性与方法’) secretfn() }}// 静态方法Pf.f1 = function(){ console.log(‘我是静态方法,只能用类直接调用,实例对象不能调用’)}Pf.prototype = { constructor:Pf,// 这种添加原型方法需要重置制定对象。 // 原型链上的方法为公有方法,由类创建出来的对象会指向该原型,不会重新创建该方法,但是优先级没有对象方法高 // 其优点是节省内存 say:function(){ console.log(“原型上的say”) // 原型链上可以拿到共有属性,拿不到私有属性与方法 console.log(‘我也能拿到数据’+this.name) } }var a = new Pf(‘ss’,22)a.say()Pf.f1()运行直接node ...

April 14, 2019 · 1 min · jiezi

promise

前言今天来分享下promise的用法,es6伟大发明之一,当初我学习的时候也是蛮头大的,不知道为啥,整个脑子就是,我在哪,我要干啥的懵圈,后面认真学习之后,觉得真是十分好用,下面就来一起学习下吧。为什么会有promise首先为什么会有promise的存在,其实很多人都知道的,其中最大的问题就是在处理多个有依赖关系的异步操作时,会出现回调地狱( callback hell ),如下:$.ajax({ url: ’….’, success: function (data) { $.ajax({ url: ’….’, success: function (data) { } }); } });promise提供了一个优雅的方式,来解决这个问题,同时提供了很多的错误捕获机制。如何使用promise我们先不讲promise的理论语法,这样会一开始就降低学习的欲望,直接来看使用案例,然后去理解。首先看基本使用new Promise(function (resolve, reject) { // 假设此处是异步请求某个数据 $.ajax({ url: ’……’, success: function (res) { if (res.code === 200) { resolve(res.data); } else { reject(‘获取data失败’); } } })}).then(function A(data) { // 成功,下一步 console.log( data); }, function B(error) { // 失败,做相应处理 console.log(error) });console:sucesserror解析:梳理流程:首先我们在promise函数里,执行我们的异步操作得到data如果成功的话,通过resolve函数数据传递出来,如果失败。通过reject把错误信息传递出来然后在.then里可以接受传递出来的数据,.then()里面接受两个函数,第一个函数接收resolve传递出来的值,也就是正确情况下的处理,第二个函数接收reject传递的信息,也就是错误的情况下的处理。Promise是一个对象,它的内部其实有三种状态。初始状态( pending )。已完成( fulfilled ): Promise 的异步操作已结束成功。已拒绝( rejected ): Promise 的异步操作未成功结束。resolve 方法可以使 Promise 对象的状态改变成成功,同时传递一个参数用于后续成功后的操作。reject 方法则是将 Promise 对象的状态改变为失败,同时将错误的信息传递到后续错误处理的操作。then(onFulfilled, onRejected)—(onFulfilled, onRejected)链式then当然,我们既然解决回调地狱,一个异步,看不出来啥优势,现在看多个异步请求, 为了代码简约,我们用setTimeout来代替ajax请求 作为异步操作,如下:new Promise((resolve, reject) => { setTimeout( () => { if (…){ resolve([1, 2, 3]) } else { reject(’error’); } }, 2000);}) .then( data => { console.log(value); // 打印出[1, 2, 3] return new Promise( (resolve, reject)=> { // 新的异步请求,需要promise对象 let data2 = 1 + data; setTimeout( () => { if (…) { resolve(data2); } else { reject(’error2’) } }, 2000); }); }, error => { cosnole.log(error) }).then( data2 => { console.log(data2 ); }, error => { cosnole.log(error) });解析:-这个例子中,第一个异步操作得到数据[1, 2, 3],传递到第一个then中,我们在第一个then中运用拿到的数据,进行第二次异步操作,并把结果传递出去。在第二个then中拿到数据,并且捕获error。可以看到本来嵌套的两个异步操作,现在清晰多了,而且链式接无数个then在这里有两个地方需要注意then里面的可捕获错误的函数,可以捕获到上面的所有then的错误,所以只在最后一个then里,写错误捕获函数就可以。每次异步操作时候需要返回一个新的promise,因为只有用promise对象才会等异步操作执行完,才去执行下面的then,才能拿到异步执行后的数据,所以第二个then里的异步请求,也需要声明Promise对象。如果then里面返回常量,可以直接返回。如下:new Promise((resolve, reject) => { setTimeout( () => { if (…){ resolve([1, 2, 3]) } else { reject(’error’); } }, 2000);}) .then( value => { return ‘222’; // 如果是直接返回常量,可直接return }) .then( value2 => { console.log(value2 ); // 打印出222 })下面忽略error情况,看两个例子,大家可以自己思考下打印结果new Promise(resolve => { setTimeout( () => { resolve(‘value1’); }, 2000);}) .then( value1 => { console.log(value1); (function () { return new Promise(resolve => { setTimeout(() => { console.log(‘Mr.Laurence’); resolve(‘Merry Xmas’); }, 2000); }); }()); return false; }) .then( value => { console.log(value + ’ world’); });value1false worldMr.Laurencenew Promise( resolve => { console.log(‘Step 1’); setTimeout(() => { resolve(100); }, 1000);}).then( value => { return new Promise(resolve => { console.log(‘Step 1-1’); setTimeout(() => { resolve(110); }, 1000); }) .then( value => { console.log(‘Step 1-2’); return value; }) .then( value => { console.log(‘Step 1-3’); return value; });}).then(value => { console.log(value); console.log(‘Step 2’);});console:Step 1Step 1-1Step 1-2Step 1-3110Step 2catchcatch 方法是 then(onFulfilled, onRejected) 方法当中 onRejected 函数的一个简单的写法,也就是说可以写成 then(fn).catch(fn),相当于 then(fn).then(null, fn)。使用 catch 的写法比一般的写法更加清晰明确。我们在捕获错误的时候,直接在最后写catch函数即可。 let promise = new Promise(function(resolve, reject) { throw new Error(“Explosion!”);});promise.catch(function(error) { console.log(error.message); // “Explosion!”});上面代码等于与下面的代码 let promise = new Promise(function(resolve, reject) { throw new Error(“Explosion!”);});promise.catch(function(error) { console.log(error.message); // “Explosion!”});异步代码错误抛出要用rejectnew Promise( resolve => { setTimeout( () => { throw new Error(‘bye’); }, 2000);}).then( value => { }).catch( error => { console.log( ‘catch’, error); });控制台会直接报错 Uncaught Error: bye解析:因为异步情况下,catch已经执行完了,错误才抛出,所以无法捕获,所以要用reject,如下:new Promise( (resolve, reject) => { setTimeout( () => { reject(‘bye’); }, 2000);}).then( value => { console.log( value + ’ world’); }).catch( error => { console.log( ‘catch’, error); });catch bye利用reject可以抓捕到promise里throw的错catch 可以捕获then里丢出来的错,且按顺序只抓捕第一个没有被捕获的错误new Promise( resolve => { setTimeout( () => { resolve(); }, 2000);}).then( value => { throw new Error(‘bye’); }).then( value => { throw new Error(‘bye2’); }).catch( error => { console.log( ‘catch’, error); });console: Error: byenew Promise( resolve => { setTimeout( () => { resolve(); }, 2000);}).then( value => { throw new Error(‘bye’); }).catch( error => { console.log( ‘catch’, error); }).then( value => { throw new Error(‘bye2’); }).catch( error => { console.log( ‘catch’, error); });console: Error: byeconsole: Error: bye2catch 抓捕到的是第一个没有被捕获的错误错误被捕获后,下面代码可以继续执行new Promise(resolve => { setTimeout(() => { resolve(); }, 1000);}) .then( () => { throw new Error(’test1 error’); }) .catch( err => { console.log(‘I catch:’, err); // 此处捕获了 ’test1 error’,当错误被捕获后,下面代码可以继续执行 }) .then( () => { console.log(’ here’); }) .then( () => { console.log(‘and here’); throw new Error(’test2 error’); }) .catch( err => { console.log(‘No, I catch:’, err); // 此处捕获了 ’test2 error’ });I catch: Error: test2 errorhereand here I catch: Error: test2 error错误在捕获之前的代码不会执行new Promise(resolve => { setTimeout(() => { resolve(); }, 1000);}) .then( () => { throw new Error(’test1 error’); }) .catch( err => { console.log(‘I catch:’, err); // 此处捕获了 ’test1 error’,不影响下面的代码执行 throw new Error(‘another error’); // 在catch里面丢出错误,会直接跳到下一个能被捕获的地方。 }) .then( () => { console.log(‘and here’); throw new Error(’test2 error’); }) .catch( err => { console.log(‘No, I catch:’, err); // 此处捕获了 ’test2 error’ });I catch: Error: test2 errorI catch: Error: another errornew Promise(resolve => { setTimeout(() => { resolve(); }, 1000);}) .then( () => { console.log(‘start’); throw new Error(’test1 error’); }) .then( () => { console.log(‘arrive here’); }) .then( () => { console.log(’… and here’); throw new Error(’test2 error’); }) .catch( err => { console.log(‘No, I catch:’, err); // 捕获到了第一个 });No, I catch: Error: test1 error at Promise.then (<anonymous>:8:1Promise.allPromise.all([1, 2, 3]) .then( all => { console.log(‘1:’, all); })[1, 2, 3]Promise.all([function () {console.log(‘ooxx’);}, ‘xxoo’, false]) .then( all => { console.log( all); }); [ƒ, “xxoo”, false]let p1 = new Promise( resolve => { setTimeout(() => { resolve(‘I'm P1’); }, 1500);});let p2 = new Promise( (resolve, reject) => { setTimeout(() => { resolve(‘I'm P2’); }, 1000); });let p3 = new Promise( (resolve, reject) => { setTimeout(() => { resolve(‘I'm P3’); }, 3000); }); Promise.all([p1, p2, p3]).then( all => { console.log(‘all’, all);}).catch( err => { console.log(‘Catch:’, err);});all (3) [“I’m P1”, “I’m P2”, “I’m P3”]案例:删除所有数据后,做一些事情、、、、db.allDocs({include_docs: true}).then(function (result) { return Promise.all(result.rows.map(function (row) { return db.remove(row.doc); }));}).then(function (arrayOfResults) { // All docs have really been removed() now!});Promise.resolvePromise.resolve() .then( () => { console.log(‘Step 1’); })其他Promise.resolve(‘foo’).then(Promise.resolve(‘bar’)).then(function (result) { console.log(result);});VM95:2 foo如果你向 then() 传递的并非是一个函数(比如 promise)它实际上会将其解释为 then(null),这就会导致前一个 promise 的结果会穿透下面How do I gain access to resultA here?function getExample() { return promiseA(…).then(function(resultA) { // Some processing return promiseB(…); }).then(function(resultB) { // More processing return // How do I gain access to resultA here? });}解决 Break the chainfunction getExample() { var a = promiseA(…); var b = a.then(function(resultA) { // some processing return promiseB(…); }); return Promise.all([a, b]).then(function([resultA, resultB]) { // more processing return // something using both resultA and resultB });}

February 27, 2019 · 5 min · jiezi

5分钟读懂JavaScript预编译流程

大家都知道JavaScript是解释型语言,既然是解释型语言,就是编译一行,执行一行,那又何来预编译一说呢?脚本执行js引擎都做了什么呢?今天我们就来看看吧。1-JavaScript运行三部曲语法分析预编译解释执行语法分析很简单,就是引擎检查你的代码有没有什么低级的语法错误; 解释执行顾名思义便是执行代码了; 预编译简单理解就是在内存中开辟一些空间,存放一些变量与函数 ;2-JS预编译什么时候发生预编译到底什么时候发生? 误以为预编译仅仅发生在script内代码块执行前 这倒并没有错 预编译确确实实在script代码内执行前发生了 但是它大部分会发生在函数执行前3-实例分析先来区分理解一下这2个概念: 变量声明 var … 函数声明 function(){}<script>var a = 1;console.log(a);function test(a) { console.log(a); var a = 123; console.log(a); function a() {} console.log(a); var b = function() {} console.log(b); function d() {}}var c = function (){console.log(“I at C function”);}console.log(c);test(2);</script>分析过程如下:页面产生便创建了GO全局对象(Global Object)(也就是window对象);第一个脚本文件加载;脚本加载完毕后,分析语法是否合法;开始预编译 查找变量声明,作为GO属性,值赋予undefined; 查找函数声明,作为GO属性,值赋予函数体;预编译//抽象描述 GO/window = { a: undefined, c: undefined, test: function(a) { console.log(a); var a = 123; console.log(a); function a() {} console.log(a); var b = function() {} console.log(b); function d() {} } }解释执行代码(直到执行调用函数test(2)语句)//抽象描述 GO/window = { a: 1, c: function (){ console.log(“I at C function”); } test: function(a) { console.log(a); var a = 123; console.log(a); function a() {} console.log(a); var b = function() {} console.log(b); function d() {} } }执行函数test()之前,发生预编译创建AO活动对象(Active Object);查找形参和变量声明,值赋予undefined;实参值赋给形参;查找函数声明,值赋予函数体;预编译之前面1、2两小步如下://抽象描述 AO = { a:undefined, b:undefined, }预编译之第3步如下://抽象描述 AO = { a:2, b:undefined, }预编译之第4步如下://抽象描述 AO = { a:function a() {}, b:undefined d:function d() {} }执行test()函数时如下过程变化://抽象描述 AO = { a:function a() {}, b:undefined d:function d() {} } —> AO = { a:123, b:undefined d:function d() {} } —> AO = { a:123, b:function() {} d:function d() {} }执行结果:注意:预编译阶段发生变量声明和函数声明,没有初始化行为(赋值),匿名函数不参与预编译 ; 只有在解释执行阶段才会进行变量初始化 ;预编译(函数执行前)创建AO对象(Active Object)查找函数形参及函数内变量声明,形参名及变量名作为AO对象的属性,值为undefined实参形参相统一,实参值赋给形参查找函数声明,函数名作为AO对象的属性,值为函数引用预编译(脚本代码块script执行前)查找全局变量声明(包括隐式全局变量声明,省略var声明),变量名作全局对象的属性,值为undefined查找函数声明,函数名作为全局对象的属性,值为函数引用预编译小结预编译两个小规则函数声明整体提升-(具体点说,无论函数调用和声明的位置是前是后,系统总会把函数声明移到调用前面)变量 声明提升-(具体点说,无论变量调用和声明的位置是前是后,系统总会把声明移到调用前,注意仅仅只是声明,所以值是undefined)预编译前奏imply global 即任何变量,如果未经声明就赋值,则此变量就位全局变量所有。(全局域就是Window)一切声明的全局变量,全是window的属性; var a = 12;等同于Window.a = 12;函数预编译发生在函数执行前一刻。 ...

January 23, 2019 · 1 min · jiezi

使用clay.js绘制一棵圆形树

作者:心叶时间:2019-01-23 17:55准备环境我们需要用到的库有三个:clay-core:提供核心的clay.js操作;clay-2d:提供Web端2d图形绘制接口;clay-chart:提供绘制常用表格的数据计算方法,这里是tree。你可以去github上下载最新的发布版本并引入,或者通过npm install安装并通过require的方式使用(具体的你可以在github上看到详细的说明)。这里,我们选择npm管理,然后引入node_modules中的文件:npm install –save clay-core clay-2d clay-chart这样,我们就准备好了npm包,接着在html中引入他们:<script src="../node_modules/clay-core/build/clay-core.js"></script><script src="../node_modules/clay-2d/build/clay-2d.js"></script><script src="../node_modules/clay-chart/build/clay-chart.js"></script>我们推荐你在实际开发中通过require方式使用,这里是为了演示方便。需要说明目前,开发环境已经准备好了,因为绘制树图,我们需要模拟数据,你可以在这里下载数据:https://github.com/yelloxing/…我们要绘制一棵圆形树,一点点显示,你可以clone最终代码,查看效果:git clone https://github.com/yelloxing/eChart.jscd eChart.jsnpm install然后在浏览器中打开 ./src/svg.tree.rotate.html 即可查看效果。计算结点位置var tree=$chart.tree({ // 目标树 “type”: “circle”, “radius”: 300, “cx”: 350, “cy”: 350, // 数据结构 “root”: initTree => initTree, “child”: parentTree => parentTree.children, “id”: treedata => treedata.name });第一步,如上面所示,配置树图的计算对象,根据原始数据计算每个结点的位置(具体的配置参数,你可以在clay-chart项目的文档中查看)。var result=tree(program.data);第二步,使用刚刚获取的树图计算对象,传递原始数据,获取包含了结点位置等信息的结果result。余下的就是绘制图形了。SVG结点绑定因为这里我们选择的是svg绘图,在绘制前,我们可以使用data方法,把数据和g标签关联起来,这样绘图更容易:var gs=$$(‘svg’).find(‘g’).data(result.node).enter(‘g’).appendTo(‘svg’);上面就把每个结点的信息挂载到g标签中,具体的api你可以查看clay-core的文档。绘图最后,我们来绘图吧!gs.loop(function (data, index, target) { window.setTimeout(function () { // 在这里绘制结点和连线条 }, index * 50);});gs就是和结点挂载起来的g标签对象,上面的loop方法会在每一个g标签上启动传递的函数,函数有三个参数,在这里分别是:data:包含位置信息的结点数据index:数据序号,也就是是第几个结点target:当前操作的g标签对象(类似是clay对象)我们发现,上面二个结点间绘制图形相差50ms,也就有了动画效果,下面在里面添加结点和连线条的绘制方法即可:// 绘制连线if (data.pid) { var pnode = $$(’[id=’ + data.pid + ‘]’); $$(’<path>’) .css({ “fill”: “none”, “stroke”: “gray” }) .attr(’d’, bezier(+pnode.attr(’left’), +pnode.attr(’top’), data.left, data.top)) .appendTo(target) }根节点没有父亲,不需要绘制连线。这里绘制连线的方法由clay-2d提供。// 绘制结点$$(’<circle stroke-width=“1” fill=“red” r=“3”>’) .attr(‘cx’, data.left).attr(‘cy’, data.top).css(“fill”, “#ea779e”) .appendTo(target);至此,就结束了,你可以在这里查看完整代码:https://github.com/yelloxing/…后记你可以看出来,clay-core提供了绘图中最基本的操作,比如对结点的增删改查和基本的计算等;而如果我们需要绘制常见的图形,比如扇形或这里的曲线等,由clay-2d提供(后期绘制3d会由clay-3d提供);而在绘制复杂图形的时候,比如这里的tree,我们需要计算每个结点的位置,就由clay-chart提供。这样的好处是灵活性高,比如这里,如果我们想使用canvas2D绘图,只需要修改绘图方法为clay-2d中提供的canvas2D方法即可! ...

January 23, 2019 · 1 min · jiezi

小程序全局变量的实现方式

小程序的一个很少人知道的全局对象引用global对象:前端开发人员对这个global对象应该不会很陌生,Node环境的时候全局对象就是这个,浏览器的全局对象是window。这个对象有什么用呢?小程序开发的时候可能经常会引用一些接口的调用、工具类的模块使用,每次调用都需要require或者import下真的好麻烦,而且很难维护,我们肯定会想能不能在一个统一的地方维护呢,global对象就可以实现。如下小程序的app.js代码:const api = require(’./utils/api.js’);const ajax= require(’./utils/tooAjax.js’);const storage= require(’./utils/storage.js’);const util = require(’./utils/util.js’);//第一种global.navH = 64;//自定义导航栏高度global.api = api;//apiglobal.ajax = ajax;//接口global.storage = storage;//本地存储global.util =util;//工具//第二种wx.api = api;//apiwx.ajax = ajax;//接口wx.storage = storage;//本地存储wx.util = util;//工具在其他页面就可以调用了哦,比如: //接口调用1 global.ajax.wearShowList().then((res) => { }); //接口调用2 wx.ajax.wearShowList().then((res) => { });

January 10, 2019 · 1 min · jiezi

defineProperty详解

可以说defineProperty是被mvvm框架激活的,虽然es5就有了但说实话了解和用的人不多下边我们来具体聊聊先从defineProperty开始说起defineproperty 操作object属性//defineproperty 有个定义object属性的功能,应该没几个人用,因为相对于obj,a = 1这种方式简直不能再难用。//通常我们定义obj属性let obj = { a:1}obj.b = 2obj[‘c’] = 3console.log(obj)//{a: 1, b: 2,c: 3} Object.defineProperty(obj,’d’,{ value: 4})console.log(obj)//{a: 1, b: 2,c: 3,d:4} //defineProperty可以定义对象属性//也可以修改Object.defineProperty(obj,‘b’,{ value: 5})console.log(obj)//{a: 1, b: 5, c: 3, d: 4}//对你没看错defineProperty有这个功能,不知可以定义新的属性还可以修改,这么逆天难用的功能为什么还要造出来?说这个有什么用?别急往下看descriptor详解defineProperty 接收三个参数object (必须有 操作的对象本身 这个很容易理解不传它操作谁?)propertyname (必须有 属性名 添加修改属性得有属性名)descriptor (必须有 官方说的我理解不了,我理解的是 属性描述1、简单点就是 设置属性的值value,2、是否可操作属性值 writable,3、是否可修改配置configurable如果值为false descriptor内的属性都不可操作)4、是否可枚举enumerable*descriptor内配置可有可无,value默认undefind,其余默认为false先做了介绍我们下边来证明下writable//栗子还是这个栗子 let obj = { a: 1 } Object.defineProperty(obj, ‘b’, { value: 2, writable: false//不可修改 }) obj.b = 3 console.log(obj) //{a: 1, b: 2} 还真是不可以 //难道是姿势不对? Object.defineProperty(obj, ‘b’, { value: 3 }) console.log(obj)//{a: 1, b: 2} 一样的效果 和姿势无关。configurable//configurable 这个比较厉害 控制descriptor内属性都不可改变不知道是不是真的//还是这个栗子 let obj = { a: 1 } Object.defineProperty(obj, ‘b’, { value: 2, //writable: false//不可修改 configurable: false }) obj.b = 5 console.log(obj)//[1,2]enumerable对否可枚举 let obj = { a: 1 } Object.defineProperty(obj, ‘b’, { value: 2, //writable: false//不可修改 //configurable: false enumerable: false }) //obj.b = 5 console.log(Object.keys(obj))//[“a”]接了下来说到重点: set和get这也是vue3.0前observe的实现原理let obj = { a: 1 } let newValue = 45 Object.defineProperty(obj, ‘b’, { get(value) { console.log(‘获取’) newVlaue = value }, set() { console.log(‘设置’) return newValue } }) obj.b = 6 //设置 obj.b //获取**写到这突然写不下去了,没有实际的应用,所以我决定延伸一篇 vue双向绑定实现。稍后跟上。。。 ...

January 4, 2019 · 1 min · jiezi

用sort实现orderby

工作到了这个年数, 感觉那些基本函数语法已经跟人合一了, 根本不会为操作一些数据结构而思考半天了. 在做小程序的时候遇到了个orderby的场景, 结果发现没有以为的那么简单. 也许是之前不求甚解的原因, 那么现在来解决orderby的问题.问题的产生与探讨方向在小程序中有个将list的某一条置顶的需求, 在初始化数据到时候可以使用数据库的orderby, 但在更新数据以后再重新初始化就显得有些不妥, 所以我尝试直接使用computed列表来解决这个问题.所以现在的问题是: 输入list, 输出orderby置顶字段.之前以为的sort很简单, 我就尝试了: arr.sort(i => i.stick). 字面看起来是根据stick字段来排序. 输出结果一团糟. 仔细思考了下又尝试了别的方法, 还是失败, 才决定仔细想一下应该如何处理.对sort的理解与快速shuffle先说一下之前对sort的理解.sort接受的参数返回大于0或者小于0. 根据结果来排序.所以有一个快速shuffle数组的方法:arr.sort(() => Math.random() - 0.5)因为函数的返回结果一半是大于0一半是小于0的(不严格, 但之后也认为概率是一半一半). 所以任何输出进行了如此处理, 都会变成一个随机顺序的数组.另外一个例子, 对一个数组: [1, 2, 3, 4, 5, 10, 11, 12]进行排序, 如果不传参数排序结果是错的, 因为默认是localCompare. 所以要写成:arr.sort((a, b) => a - b)这样才能得到正确从小到大的排列.以上就是我多年以来对sort的所有理解. 所以才会写出上面的: arr.sort(i => i.stick)这样搞笑的东西. 因为理解是有问题的.sort是如何排序的因为不知道sort函数得到了结果后是如何排序的. 所以对sort的理解有问题. 而我们知道reduce就是从头到尾遍历并传递每次计算的结果. sort却不知道. 所以打出每次的返回值来看一下每次得到返回值后sort做了些什么.我们要对不同数组进行同样的操作, 排序方法是一样的, 先写一下:const log = (a, b) => { console.log(a, b, a - b > 0) return a - b}开始对不同数组进行排序: 先来1到5[1, 2, 3, 4, 5].sort(log)结果: [1, 2, 3, 4, 5]2 1 true3 2 true4 3 true5 4 true尝试: 从5到1[5, 4, 3, 2, 1].sort(log)结果: [1, 2, 3, 4, 5]4 5 false3 4 false2 3 false1 2 false目前看来, sort应该是插入排序.[3, 5, 7, 9, 2, 1, 6].sort(log)看log的时候我把当前排序结果也打一下:5 3 true [3, 5]7 5 true [3, 5, 7]9 7 true [3, 5, 7, 9]2 9 false // 2还是与当前最大的9比.结果第一次false2 7 false // 于是一路比下来2 5 false2 3 false // 比到最小的, 于是确定了位置 [2, 3, 5, 7, 9]1 5 false // 1选择了与5比, 此时5是中间位置的数, 而不是最大的数1 3 false // 然后一个一个比较下来1 2 false [1, 2, 3, 5, 7, 9]6 5 true // 6还是于5比, 此时5也是中间位置的数6 9 false // 没有选择与7, 而是与9比了6 7 false从这些log能得出一些粗浅的结论:sort是插入排序每次比较的数字会根据两个因素来决定: 分别是之前比较的结果和当前排序的位置如何实现orderby首先明确思路:sort认为每个元素之间的关系是比大小, 所以我们需要做的是写出任意两个元素的相对顺序的普遍公式.先构建一组数据:let gnrt = () => ({ age: Math.round(Math.random() * 50), height: Math.round(Math.random() * 200) })let arr = Array.from({length: 10}).map(() => gnrt())我们先建立纯数字, 无顺序的orderby来理这个思路.let orderby = function (arr, …orders) { return arr.sort((a, b) => { let res = 0 for (let order of orders) { if (a[order] - b[order] !== 0) { res = a[order] - b[order] break } } return res })}调用orderby(arr, ‘height’, ‘age’)就得到了理想的orderby结果了: 根据权重排序, 如果都一样就保持顺序.#后续#这个思路清晰以后, 做兼容就容易了:如果要指定顺序, 在排序参数里带特征, 例如’height’, ‘-height’, 来决定在执行的时候是a - b 还是b - a.如果要指定排序函数(在非数字情况下). 把排序参数改写成兼容function的, 判断是string就执行默认, 是function就调用function即可.当然, 功能越完善的函数就越复杂, 函数本身只是函数复杂度和业务复杂度交换的作用. 具体实现就不写了.所以置顶排序如何实现我们已经想清楚了orderby的实现, 那么置顶排序是stick这个布尔值字段, 就必须根据我上面说的传函数进去, 并且改写orderby函数.这样又要多些2个函数, 所以我选择:[…arr.filter({stick} => stick), …arr.filter({stick} => !stick)]搞定. ...

December 22, 2018 · 2 min · jiezi

不定参数(rest 参数 ...)

不定参数如何实现不定参数使用过 underscore.js 的人,肯定都使用过以下几个方法:.without(array, *values) //返回一个删除所有values值后的array副本.union(*arrays) //返回传入的arrays(数组)并集_.difference(array, *others)//返回来自array参数数组,并且不存在于other 数组…这些方法都有一个共同点,就是可以传入不定数量的参数,例如,我想删除掉 array 中的 value1,value2 ,可以这样使用 , .without(array,value1,value2);那么,这个需要怎样才能做到呢?我们知道,js 中 function 里面,有一个 arguments 参数,它是一个类数组,里面包含着调用这个方法的所有参数,所以可以这样处理:.without = function() { if (arguments.length > 0) { var array = arguments[0]; var values = []; for (var i = 1; i < arguments.length; i++) { values.push(arguments[i]); } //这样得到了array,和values数组,便可以进一步处理了 }};上面只是打个比方,想要支持不定参数,我们要做的就是把固定参数和动态参数从 arguments 中分离出来。但是,我们这样写的话,需要在每个支持不定参数的函数里,都 copy 这样一段代码,这样实在不是很优雅。所以需要封装成一个通用的函数。我们直接看看 underscore 是封装的好了。restArgs 源码var restArgs = function(func, startIndex) { //startIndex ,表示几个参数之后便是动态参数 startIndex = startIndex == null ? func.length - 1 : +startIndex; return function() { var length = Math.max(arguments.length - startIndex, 0); //处理arguments,将动态参数保存进rest数组 var rest = Array(length); for (var index = 0; index < length; index++) { rest[index] = arguments[index + startIndex]; } //处理0,1,2三种情况,这里要单独处理,是想优先使用call,因为,call的性能比apply要好一点 switch (startIndex) { case 0: return func.call(this, rest); case 1: return func.call(this, arguments[0], rest); case 2: return func.call(this, arguments[0], arguments[1], rest); } //如果startIndex不是0,1,2三种情况,则使用apply调用方法,将args作为参数,args将为数组[固定参数 ,rest]; var args = Array(startIndex + 1); for (index = 0; index < startIndex; index++) { args[index] = arguments[index]; } args[startIndex] = rest; return func.apply(this, args); };};//这里without主要的逻辑处理方法,作为参数,传给restArgs,在restArgs中处理完参数后,使用call或apply调用逻辑处理方法// 这时候接受到参数otherArrays,已经是一个数组了,包含了之前的动态参数。_.without = restArgs(function(array, otherArrays) { //处理without具体事件});underscore.js 中利用 js 高级函数的特性,巧妙的实现了动态参数如果要使某函数支持不定参数,只需要将该函数作为参数,传入 restArgs 中即可,例如:function addByArray(values) { var sum = 0; for (var i = 0; i < values.length; i++) { sum += values[i]; } return sum;}var add = restArgs(addByArray);//调用:addByArray([2, 5, 3, 6]); //16add(2, 5, 3, 6); //16ES6 不定参数 (…)ES6 引入了 rest 参数,(形式为"…变量名"),用于获取多余参数,这样就不需要使用 arguments 对象来实现了function add(…values) { let sum = 0; for (var val of values) { sum += val; } return sum;}add(2, 5, 3); // 10总结在 underscore 中,restArgs 只是为了支持不定参数。实际使用中,也许我们都是直接使用 ES6,或用 babel 将 ES6 转成 ES5 来支持不定参数不过,如果是在非 es6 的环境下,知道有这么一种实现方式,也是挺好的。:) ...

December 21, 2018 · 2 min · jiezi

JS 总结之关于 this 应该知道的几个点

this 对每个 Jser 都不陌生,经常看到对象这里 this 那里 this,那什么是 this?答案就是上下文对象,即被调用函数所处的环境,也就是说,this 在函数内部指向了调用它(它指函数函数)的对象,通俗的讲,就是谁调用了函数。???? 情况 1this 指向 windowvar name = ‘xiaoming’ // 思考,为什么不能用 let 或者 const ?function foo () { console.log(this.name)}foo() // xiaoming谁调用了这个函数,答案就是 window。这好理解,因为这里的变量和函数都是直接挂在 window 上的,等同于 window.foo()。需注意,严格模式下,this 为 undefined???? 情况 2this 指向一个对象var name = ‘xiaoming’var foo = { name: ‘Jon’, getName () { console.log(this.name) }}foo.getName() // Jon谁调用了这个函数,答案是 foo 对象,所以打印了 Jon 而不是 xiaomingvar bar = foo.getNamebar() // xiaoming如果赋值到另一个变量,就变成 window 调用,所以打印了 xiaoming???? 情况 3this 指向了一个用 new 新生成的对象function Person (name) { this.name = name this.getName = function () { console.log(this.name) }}var jser = new Person(‘Jon’)jser.getName() // Jon这种方式成为 new 绑定,也叫做构造调用。JavaScript 中,new 的机制实际上和面向类的语言完全不同,构造函数只是一些使用 new 操作符时被调用的函数,它们并不会属于某个类,也不会实例化一个类。实际上,除 ES6 的 Symbol()外,所有函数都可以用 new 来调用,所以并不存在所谓的构造函数,只有对于函数进行构造调用。使用 new 来调用函数,或者说发生构造函数调用时,会自动执行下面的操作:创建(或者说构造)一个全新的对象这个新对象会被执行原型链接这个新对象会绑定到函数调用的 this如果函数没有返回其他对象,那么 new 表达式中的函数调用会自动返回这个新对象这个例子中,使用 new 来调用 Person(..) 时,我们会构造一个新对象(jser)并把它绑定到 Person(..) 调用中的 this 上。或者可以这么想,谁调用了 getName ?就是 jser 调用了,所以 this 指向了 jser???? 情况 4用 call,apply 和 bind 来修改 this 指向call / applycall 和 apply 是以不同对象作为上下文对象来调用某个函数,举个例子:var bar = { name: ‘bar’, getName () { console.log(this.name) }}var foo = { name: ‘foo’}bar.getName.call(foo) // foo看起来像是借用函数,对象 foo 借用了 bar 的函数 getName,所以我们判断一个对象类型,经常这么搞:let foo = [1,2,3,4,5]Object.prototype.toString.call(foo) // “[object Array]“apply 和 call 的用法一样,不同点在于 call 用参数表给调用函数传参,而 apply 使用了数组bindbind 可以永久性的修改函数中的 this 的指向,无论谁调用,this 指向都一样,并返回了完成绑定的函数,看例子:var bar = { name: ‘bar’, getName () { console.log(this.name) }}var foo = { name: ‘foo’}foo.func = bar.getName.bind(bar)foo.func() // bar这里的 func 不受 foo 影响,this 还是指向了 barvar bar = { name: ‘bar’, getName () { console.log(this.name) }}func = bar.getName.bind(bar)func() // bar这里的 func 也不受 window 影响,this 还是指向了 bar综合上述,bind 强制修改了 this,谁调用了函数 this 都不能被修改???? 忽略 this如果你把 null 或者 undefined 作为 this 的绑定对象传入 call、apply 或者 bind,这些值再调用时会被忽略,实际应用的是默认绑定。???? 箭头函数ES6 中介绍了一种无法使用这些规则的特殊函数类型:箭头函数,根据外层(函数或者全局)作用域来决定 this,箭头函数常用于回调函数???? 情况 1 中,为什么不能用 let 声明?ES6 中,let 命令、const 命令、class 命令声明的全局变量,不属于顶层对象的属性,window 无法访问到。var 命令和 function 命令声明的全局变量,属于顶层对象的属性,window 能访问到。所以 情况 1 中改为:let name = ‘xiaoming’function foo () { console.log(this.name)}foo() // undefined❄️ 总结自:《你不知道的 JavaScript 上卷》第二部分 第 2 章《Node.js 开发指南》附录 A《ECMAScript 6 入门》let 和 const 命令 - 顶层对象的属性【进阶 3-5 期】深度解析 new 原理及模拟实现前端总结小本本 更新时间为:周二,周四,周六,内容不定。如想查看最新未完成总结,可请查看:https://github.com/KaronAmI/blog如有启发,欢迎 star,如有错误,请务必指出,十分感谢。???? ...

December 20, 2018 · 2 min · jiezi