前端学习

前端学习

@色少5年前

11/29
21:24
前端技能

微信小程序渲染html内容

最近又做了一个新的小程序关于物流订单查询欢迎来体验

 

遇到了一个小问题:数据中返回电话号码的字符串识别出来并且高亮和可以绑定事件。比如数据中包含您的派送员黄xx正在派件,电话:137xxxx41460已经在派送。其中就要识别出137xxxx41460并且绑定点击事件可以点击拨打电话号码。

对于这个功能搜集了不少资料其中包含了3个解决方案但是各有优缺点因此记录下来

wxParse

小程序刚上线那会儿,是无法直接渲染HTML内容的,于是就诞生了一个叫做「 wxParse 」的库。它的原理就是把HTML代码解析成树结构的数据,再通过小程序的模板把该数据渲染出来。

rich-text

后来,小程序增加了「rich-text」组件用于展示富文本内容。然而,这个组件存在一个极大的限制: 组件内屏蔽了所有节点的事件 。也就是说,在该组件内,连「预览图片」这样一个简单的功能都无法实现。

web-view

再后来,小程序允许通过「web-view」组件嵌套网页,通过网页展示HTML内容是兼容性最好的解决方案了。然而,因为要多加载一个页面,性能是较差的,个人小程序也是没法用webview。

 

因为我这个只是需求只是需要识别出来电话号码并且不是那种复杂的字符模板。因此参考wxparse 的原理自己写了一个关于电话号码识别的功能。
效果如下:

技术重点就是

1、在数据返回后对于字符串用正则(/([^\d]+)|(\d{9,14})|(\d{3,4}\-\d{6,10})/g)识别出电话号码存在一个新的字段并且表示为type:phone ,不是电话号码的字符就放到另外一个字段并且表示为type:text.数据结构如图所示

2、新建一个关于字段读取的模板

1
2
3
4
5
6
7
8
9
10
11
<template name="wepyhtml">
  <block wx:for="{{wxmlData}}" wx:key="">
    <template is="wxml" data="{{item}}"></template>
  </block>
</template>
<template name="wxml">
  <block wx:if="{{item.type == 'phone'}}" wx:key="">
    <text class="blue" bindtap="tapTel" data-phone="{{item.acceptStation}}">{{item.acceptStation}}</text>
  </block>
  <block wx:else>{{item.acceptStation}}</block>
</template>

其中,在模板里面绑定点击事件方便后期做逻辑处理还可以绑定不同的参数方便获取

3、在需要用到的地方引入模板

1
2
  <import src="/components/html.wxml" />  
  <template is="wepyhtml" data="{{wxmlData:item.nodes}}"></template>

那就满足现在的功能呢需求了。不用复杂的插件也比直接引入原生组件来得方便。关键就是思路。。特此记录哈。

微信小程序渲染html内容

@色少6年前

10/22
20:45
前端技能

2019最新个税计算器 / 个税计算器 wepy 开发

1、扫一扫

2、效果

        

 

3、官网API:https://developers.weixin.qq.com/miniprogram/dev/api/

4、wepy框架 :https://tencent.github.io/wepy/

5、开发问题记录

(1)、在开发微信小程序组件框架时,我遇到了一个问题,微信小程序中的button组件有特定的css,背景可以用“backgroundnone”去掉,但是边框再用“border : none”去掉就不可以了,这也是微信小程序与h5的不同之处。

但是在微信小程序中使用:after选择器就可以实现这一功能。

/*使用 button::after{ border: none; } 来去除边框*/

(2)、计算需要各个基数可以从各大城市的社保网站扒下来放进数据库,统一保存利用接口返回计算(不同城市有不同基数,如果没有按照全国返回)

(3)、获取本地location方便了解知道哪个城市返回其基数做统计。用到wepy.getSetting  获取其返回值中只会出现小程序已经向用户请求过的权限。判断是否授权并且获取经纬度,利用腾讯地图接入(定位,位置转经纬度)返回其城市。

(4)、数据请求过慢导致页面闪烁,可以在请求前利用wx.showLoading();加载load,在data里面增加参数控制不显示demo内容(load:true)隐藏页面没加载数据的静态节点。数据返回时调用wx.hideLoading(); 隐藏load图案,并且把data里面的参数设置为(load:false).一次性显示加载好的数据和静态节点。

 

 

 

2019最新个税计算器 / 个税计算器 wepy 开发

@色少6年前

10/22
20:36
前端技能

[微信小程序] 微信小程序经纬度逆解析地理位置

因为需要在小程序加个定位并加载对应城市信息

然而小程序自带api目前只能获取经纬度不能逆解析,因此借用腾讯的地图服务做一次解析。

需求:微信小程序获取用户定位信息并加载对应城市信息

技术:wx.getLocation  、腾讯地图小程序api 、 微信小程序经纬度逆解析地理信息

 

首先登陆腾讯地图 点击   webservice  api   选择 逆地址解析(坐标位置描述)  

ps:注册腾讯地图授权key 是最早要做的事情

可以选择调用WebService API  或者    微信小程序JavaScript SDK 2种方式目前调用次数是一样的

 

贴出JavaScript SDK代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
//加载腾讯位置服务js文件(必须)

var qqmap=require('../../utils/qqmap-wx-jssdk.min.js')

//预加载内容
getLocation () {

  //调用腾讯地图sdk 添加腾讯地图key

   var demo = new qqmap({
     key: '*****-SY7PH-*****-*****-*****-SMFTK'
    });
  //加载腾讯位置服务js文件(必须)

    //此声明最好不要写在 wx.getLocation方法内,this指代不同

    var that = this;
    wx.getLocation({
      type: 'gcj02',  //编码方式,
      success: function (res) {
        var latitude = res.latitude   // wx.getLocation 获取当前的地理位置、速度   latitude(维度)  longitude(经度)
        var longitude = res.longitude
        demo.reverseGeocoder({  
                //腾讯地图api 逆解析方法 首先设计经纬度
          location: {
            latitude: res.latitude,
            longitude: res.longitude
          },
          //逆解析成功回调函数
          success: function (res) {
            getApp().globalData.cityname = res.result.address_component.city;   //全局变量存放城市   res.result.address_component.city 获取解析之后地址中的城市 33            
          console.log("onLoad");

          //wx.request 发起网络请求,类似于ajax
            wx.request({
              url: 'https://www.xxxxxxxxxx.com/home/index/xcx_index',  //填写对应服务器地址
              data: {
                // cityid: getApp().globalData.cityid,     //我这里需要提交的参数,这里我是把获取的城市发给后台,然后后台给我对应城市的数据json文件
                cityna: getApp().globalData.cityna,
                district: res.result.address_component.city,
              },
              header: {
                "Content-Type": "applicatiSon/x-www-form-urlencoded"
              },
              method: "POST",
              success: function (res) {
                console.log(res.data.data);  
              //解析回调函数得到的json文件并一层一层的解析然后数据绑定到页面上
                that.setData({
                  // 轮播图图片
                  imgurls: res.data.data.pics 58                 });
                getApp().globalData.cityid = res.data.data.datacompany.cityid;
                // this.data.pic_array_dq2[e.detail.value].name
              }
            });
  ·        // 成功后提醒
            wx.showModal({
              title: '定位成功',
              content: '当前城市:' + getApp().globalData.cityname,
            });
           
          },
          fail: function (res) {
          //失败的回调函数
            // console.log(res);
          },
          complete: function (res) {
          //完成之后的回调函数,不管是否成功
            console.log("逆解析状态码:"+res.status);  //res.status  获取状态码,为0代表解析成功,但是我们要考虑失败的兼容,可能用户并不愿意同意获取地理位置,所以不为0 的时候也要加载内容给个默认地址
            if (res.status!=0){
              wx.request({
                url: 'https://www.cyzs97.com/home/index/xcx_index',
                data: {
                  // cityid: getApp().globalData.cityid,
                  cityna: getApp().globalData.cityna,
                  district:"",
                },
                header: {
                  "Content-Type": "applicatiSon/x-www-form-urlencoded"
                },
                method: "POST",
                success: function (res) {
                  console.log(res.data.data);
                  that.setData({
                    // 轮播图图片
                    imgurls: res.data.data.pics,   99                   });
                  getApp().globalData.cityid = res.data.data.datacompany.cityid;
                  // this.data.pic_array_dq2[e.detail.value].name
                }
              });
            //提示用户失败可开启定位服务
              wx.showModal({
                title: '定位失败',
                content: '定位失败,未授权获取当前位置或服务错误' ,
              });
            }
            console.log("定位获取的:" + getApp().globalData.cityname);
          }
        });
      }
    });
       
    },

调用配合授权一并调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
    getLocation(){
        var that = this ;
          wepy.getSetting({
            success: (res) => {
              if (res.authSetting['scope.userLocation'] != undefined && res.authSetting['scope.userLocation'] != true) {//非初始化进入该页面,且未授权
                wepy.showModal({
                  title: '是否授权当前位置',
                  content: '需要获取您的地理位置,请确认授权,否则无法获取您所需数据',
                  success: function (res) {
                    if (res.cancel) {
                      that.isshowCIty = false;
                      wepy.showToast({
                        title: '授权失败',
                        icon: 'success',
                        duration: 1000
                      })
                    } else if (res.confirm) {
                      wepy.openSetting({
                        success: function (dataAu) {
                          if (dataAu.authSetting["scope.userLocation"] == true) {
                            wepy.showToast({
                              title: '授权成功',
                              icon: 'success',
                              duration: 1000
                            })
                            //再次授权,调用getLocationt的API
                            that.getLocation();
                          } else {
                            wepy.showToast({
                              title: '授权失败',
                              icon: 'success',
                              duration: 1000
                            })
                          }
                        }
                      })
                    }
                  }
                })
              } else if (res.authSetting['scope.userLocation'] == undefined) {//初始化进入
                that.getLocation();
              }
              else { //授权后默认加载
                that.getLocation();
              }
            }
          })
      },

当然最重要的不能忘记,需要配置服务器,只需安全域名设置,需要在微信公众平台添加域名地址 https://apis.map.qq.com
还有如果是WebService API 记得配置授权里面配置白名单添加你服务器的ip地址~~~切记~

[微信小程序] 微信小程序经纬度逆解析地理位置

@色少6年前

03/20
15:56
前端技能

Wepy-小程序踩坑记

引言

  • 用过原生开发的小程序也知道除了api 其他功能性的内容并不多对于需要做大型项目来说是比较难入手的,因此朋友推荐的wepy我就入坑鸟。。。
  • 这么一个跟vue的开发方式类似的框架,不过说起来跟vue类似,但是用起来还真不是那么简单。api开发还是和部分vue有出入,因此如下记录,入手的教程就不发了只发踩坑。

官方文档

下面是已经踩过的坑

 wepy 问题查找

标签中的指令简写

  • Vue类似
    • 对于动态赋值的属性可以使用 :attr=”value” 的方式
    • 对于绑定事件可以使用@click=”fn”的方式
  • data使用注意点
    • 对于视图中需要用到的数据,应该事先在data中定义一下,哪怕此时没有数据,也应该定义一个空值

WePY中的methods的使用

  • 只能声明页面的bindcatch事件,不能声明自定义方法
  • 自定义方法应该跟methods平级

this 运用

  • 小程序里修改data 里面的属性或者赋值都需要利用this.setdata wepy 基本就是利用this.属性即可。如果是异步返回或者更新dom需要this.$apply() 触发脏值检测

页面跳转

  • navigateTo()  redirectTo() 差别。
  •  navigateTo()保留当前页面,跳转到应用内的某个页面(即:显示顶部导航栏左上角返回按钮,可以有返回路径)
  • redirectTo()关闭当前页面,跳转到应用内的某个页面(即:不显示左上角返回按钮,如需要返回在页面内自己添加按钮写路径或者利用wx.navigateBack()和 getCurrentPages() 获取当前的页面栈,决定需要返回几层。
  • 简单来说如果你需要tabbar有返回就用navigateTo,不需要就用redirectTo
  • 只能用 switchTab() 跳转到 tabbar 页面

文件上传

  • 上传文件没有传统html中的文件域(<input type=”file”/>),要想上传文件只能使用API uploadFile()

更新DOM$apply

  • 如果需要更新DOM,应该在随后调用组件实例的$apply方法,才能更新到页面中
this.name=”abc”;
this.$apply()
  • PS:对于性能要求较高的应用,不要频繁的调用$apply()更新DOM,可以根据实际情况更新父组件向子组件传递数据,通过props的方式传递
  • 如果需要传递动态数据,加上.sync的修饰符就可以解决(:prop.snyc=’item’
  • 如果需要子组件数据同步到父组件,需要在定义props的时候设置twoWay:true
  • (所有异步数据传递必须用$apply,同步的话才能使用.sync)

mixin

  • wepymixin,与vue中的mixin执行顺序相反
    • wepy中,会先执行组件自身的,再执行mixin中的
    • vue中对于钩子函数,会先执行mixin中的,再执行组件自身的;vuemethods如果和mixin同名,那么只会执行自身的方法

关于canvasbase64

  • 小程序中可以进行canvas相关操作,但是跟纯html中的canvas有所不同(api差异),canvas的使用都应该参照:小程序中的canvas

arrayBufferbase64互转

  • 本段内容在文档中是搜索不到的,但是确实是支持的,使用如下2种方式:
    • wx.arrayBufferToBase64(arrayBuffer)
    • wx.base64ToArrayBuffer(base64)

命名规范

  • 小程序内部定义的实例API都以$开头,所以我们在定义实例属性、方法的时候不能以$开头,以便区分

同名组件共享同一实例及数据

循环渲染组件时,容易出现组件数据相互污染。可以用最外层的组件监听事件冒泡以修改数据,同时触发事件的组件用setTimeout包裹,保证执行顺序。

循环渲染组件:

wepy的循环渲染组件,必须使用 <repeat>标签,或者微信官方的<block>标签(这两个标签都是不会渲染到dom的)否则就不会渲染成功。

组件component 没有 onLoad 等页面事件

页面中设置好 this.$broadcast(‘someEvent’, option);
组件监听事件则可以解决

page

页面类,继承自wepy.component,拥有页面所有的属性与方法。
全部属性继承自wepy.component。而wepy.component没有onLoad 等方法

Wepy-小程序踩坑记

@色少7年前

07/21
13:08
前端技能

React-native 异常

最近在捣鼓下rn,刚入门就一堆错误并且再xcode里面展示看到一头雾水。网上资料不是很多,对于前端来说简直就是入门程度难上加难。因此自我做个记录。
目前开发环境:
xcode:8.3.3
node:8.1.2
react-native-cli: 2.0.1
react-native: 0.45.1

新版本的rn 0.45系列一启动xcode模拟容易出错关键在于加载的包的下载问题-(要不就降级版本要不就是一步步查找缺失的文件包等)。

ios
一启动就报错

unpacking /.rncache/glog-0.3.4.tar.gz 关键字

产生原因:

/Users/mocoyu/.rncache 中 boost_1_63_0.tar.gz, double-conversion-1.1.5.tar.gz, folly-2016.09.26.00.tar.gz, glog-0.3.4.tar.gz 基本文件下载不完整
node_modules/react-native/third-party 文件不完整

以上基本都是第三方的模块没下载下来。

对于这种要不是没正确上网姿势(正确的翻wa11)要不就是下载不完全
因此解决办法:
1、删除 .rncache 后重新下载,或手动下载后放入 .rncache 中
2、把以上文件解压后放入 node_modules/react-native/third-party 下
3、Clean & Build

.rncache 文件位置在:

显示系统隐藏文件办法,终端输入:
defaults write com.apple.finder AppleShowAllFiles -bool true //显示隐藏文件
defaults write com.apple.finder AppleShowAllFiles -bool false //不显示隐藏文件
命令运行之后需要重新加载Finder:快捷键option+command+esc,选中Finder,重新启动即可

 

如果以上你都解决不了那就是wa11问题,你需要如下操作
1、https://codeload.github.com/google/glog/tar.gz/v0.3.4
下载完毕后 放入/Users/YOUCOUNT/.rncache
2、开启了全局模式的墙,delete node_modules再从新执行一边安装
3、building前再切换墙自动代理模式(不然会有坑,比如make sure you’re running a packager server or have included a .jsbundle file in your application bundle的错误)

缺失哪些文件就在google的codeload里面找对应版本下载。

查看详细的Xcode编译错误

左侧点击跟项目目录 -> 选择右侧 Build Settings -> 选择 All & Combined -> 搜索框输入 Always Search User Paths -> 将 Always Search User Paths 设置为 Yes -> Clean -> Build

make sure you’re running a packager server or have included a .jsbundle file in your application bundle

基本遇到此类错误就是没有正确的翻 wa11,去把此处Mac中用于科学上网的shadowocks中的网络代理,从 全局代理 改为 自动代理模式 即可

React-native 异常

@色少7年前

05/11
19:17
前端技能

函数节流(throttle)与函数去抖(debounce)

项目优化种发现很多时候需要输入之后验证,因此用得频率比较高的2个函数debounce和throttle。还有以下场景往往由于事件频繁被触发,因而频繁执行DOM操作、资源加载等重行为,导致UI停顿甚至浏览器崩溃。

1. window对象的resize、scroll事件

2. 拖拽时的mousemove事件

3. 射击游戏中的mousedown、keydown事件

4. 文字输入、自动完成的keyup事件

实际上对于window的resize事件,实际需求大多为停止改变大小n毫秒后执行后续处理;而其他事件大多的需求是以一定的频率执行后续处理。针对这两种需求就出现了debounce和throttle两种解决办法。

debounce

debounce的关注点是空闲的间隔时间

 // 函数去抖(连续事件触发结束后只触发一次)
 // sample 1: _.debounce(function(){}, 1000)
 // 连续事件结束后的 1000ms 后触发
 // sample 1: _.debounce(function(){}, 1000, true)
 // 连续事件触发后立即触发(此时会忽略第二个参数)

_.debounce = function(func, wait, immediate) {
    // immediate默认为false
    var timeout, args, context, timestamp, result;

    var later = function() {
      // 当wait指定的时间间隔期间多次调用_.debounce返回的函数,则会不断更新timestamp的值,导致last < wait && last >= 0一直为true,从而不断启动新的计时器延时执行func
      var last = _.now() - timestamp;

      if (last < wait && last >= 0) {
        timeout = setTimeout(later, wait - last);
      } else {
        timeout = null;
        if (!immediate) {
          result = func.apply(context, args);
          if (!timeout) context = args = null;
        }
      }
    };

    return function() {
      context = this;
      args = arguments;
      timestamp = _.now();
      // 第一次调用该方法时,且immediate为true,则调用func函数
      var callNow = immediate && !timeout;
      // 在wait指定的时间间隔内首次调用该方法,则启动计时器定时调用func函数
      if (!timeout) timeout = setTimeout(later, wait);
      if (callNow) {
        result = func.apply(context, args);
        context = args = null;
      }

      return result;
    };
  };

Throttle

throttle 的关注点是连续的执行间隔时间

// 函数节流(如果有连续事件响应,则每间隔一定时间段触发) 
// 每间隔 wait(Number) milliseconds 触发一次 func 方法
// 如果 options 参数传入 {leading: false} 
// 那么不会马上触发(等待 wait milliseconds 后第一次触发 func) // 如果 options 参数传入 {trailing: false}
// 那么最后一次回调不会被触发
// **Notice: options 不能同时设置 leading 和 trailing 为 false** 
// 示例: 
// var throttled = _.throttle(updatePosition, 100);
// $(window).scroll(throttled); 
// 调用方式(注意看 A 和 B console.log 打印的位置):
// _.throttle(function, wait, [options]) 
// sample 1: _.throttle(function(){}, 1000)
// print: A, B, B, B ... 
// sample 2: _.throttle(function(){}, 1000, {leading: false}) 
// print: B, B, B, B ... 
// sample 3: _.throttle(function(){}, 1000, {trailing: false}) 
// print: A, A, A, A ...
 // ----------------------------------------- //


_.throttle = function(func, wait, options) { 

/* options的默认值 * 表示首次调用返回值方法时,会马上调用func;否则仅会记录当前时刻,当第二次调用的时间间隔超过wait时,才调用func。
 * options.leading = true; 
 * 表示当调用方法时,未到达wait指定的时间间隔,则启动计时器延迟调用func函数,若后续在既未达到wait指定的时间间隔和func函数又未被调用的情况下调用返回值方法,则被调用请求将被丢弃。
 * options.trailing = true; 
 * 注意:当options.trailing = false时,效果与上面的简单实现效果相同 
*/

   var context, args, result;
    var timeout = null;
    var previous = 0;
    if (!options) options = {};
    var later = function() {
      previous = options.leading === false ? 0 : _.now();
      timeout = null;
      result = func.apply(context, args);
      if (!timeout) context = args = null;
    };
    return function() {
      var now = _.now();
      if (!previous && options.leading === false) previous = now;
      // 计算剩余时间
      var remaining = wait - (now - previous);
      context = this;
      args = arguments;
      // 当到达wait指定的时间间隔,则调用func函数
      // 精彩之处:按理来说remaining <= 0已经足够证明已经到达wait的时间间隔,但这里还考虑到假如客户端修改了系统时间则马上执行func函数。
      if (remaining <= 0 || remaining > wait) {
        // 由于setTimeout存在最小时间精度问题,因此会存在到达wait的时间间隔,但之前设置的setTimeout操作还没被执行,因此为保险起见,这里先清理setTimeout操作
        if (timeout) {
          clearTimeout(timeout);
          timeout = null;
        }
        previous = now;
        result = func.apply(context, args);
        if (!timeout) context = args = null;
      } else if (!timeout && options.trailing !== false) {
        // options.trailing=true时,延时执行func函数
        timeout = setTimeout(later, remaining);
      }
      return result;
    };
  };

throttle 应用场景

  • DOM 元素的拖拽功能实现(mousemove)
  • 射击游戏的 mousedown/keydown 事件(单位时间只能发射一颗子弹)
  • 计算鼠标移动的距离(mousemove)
  • Canvas 模拟画板功能(mousemove)
  • 搜索联想(keyup)
  • 监听滚动事件判断是否到页面底部自动加载更多:给 scroll 加了 debounce 后,只有用户停止滚动后,才会判断是否到了页面底部;如果是 throttle 的话,只要页面滚动就会间隔一段时间判断一次

debounce 应用场景
函数去抖有哪些应用场景?哪些时候对于连续的事件响应我们只需要执行一次回调?

  • 每次 resize/scroll 触发统计事件
  • 文本输入的验证(连续输入文字后发送 AJAX 请求进行验证,验证一次就好)

函数节流(throttle)与函数去抖(debounce)

@色少7年前

05/11
13:35
前端技能

深入剖析 JavaScript 的深复制

http://www.cnblogs.com/charling/p/3452677.html
jQuery.extend()源码解读深拷贝或者浅拷贝
http://jerryzou.com/posts/dive-into-deep-clone-in-javascript/
深入剖析JavaScript 的深复制

记录如下:
1、jQuery.clone()——dom对象的复制
在 jQuery 中也有这么一个叫

1
$.clone()

的方法,可是它并不是用于一般的 JS 对象的深复制,而是用于 DOM 对象。
2、jQuery.extend()——深复制(递归extend)
3、lodash —— _.clone() / _.cloneDeep()

1
_.clone(obj, true)

等价于

1
_.cloneDeep(obj)

比较各个深复制方法

特性 jQuery lodash JSON.parse 所谓“拥抱未来的深复制实现”
浏览器兼容性 IE6+ (1.x) & IE9+ (2.x) IE6+ (Compatibility) & IE9+ (Modern) IE8+ IE9+
能够深复制存在环的对象 抛出异常 RangeError: Maximum call stack size exceeded 支持 抛出异常 TypeError: Converting circular structure to JSON 支持
对 Date, RegExp 的深复制支持 × 支持 × 支持
对 ES6 新引入的标准对象的深复制支持 × 支持 × ×
复制数组的属性 × 仅支持RegExp#exec返回的数组结果 × 支持
是否保留非源生对象的类型 × × × 支持
复制不可枚举元素 × × × ×
复制函数 × × × ×

深入剖析 JavaScript 的深复制

@色少9年前

06/23
13:35
前端技能

跨域小妙招总结

一直都说想找个大总结的文章关于跨域的处理奈何度娘上的资料太老旧,自己对于新的知识点又不是十分十分有研究。因此一直让懒癌发作下去。奈何最近做了双失青年,因此有了空余的时间去研究研究知识点和梳理一下,顺便自己动手风衣足食。废话不多说请看下文。提交上github的分享。希望可以多多讨论。

https://github.com/yusemo/javascript-cros/

 

 

跨域小妙招总结

@色少9年前

05/20
13:28
前端技能

数组去重的一些总结

废话不多说直接上题目!关于数组去重的例子

数组arr1和arr2,长度不固定,如何从arr1中删除和arr2中重复值的元素?

如:


var arr1=[1,3,4,6,7];

var arr2=[3,4,7];

====>

var arr3=[1,6]

console.log()

//*  ==================================================================================*//

思路1:

假如数组中的元素全是数字可以直接利用正则匹配

console.log(arr1.concat(arr2).sort().join(”).replace(/(w)1+/g,”).split(”))

思路2:

数组中包含其他类型的字符,比如:

var arr=[1,2,3,’1′,’2′,’3′,1,2]

利用hash表。把已经出现过的通过下标的形式存入一个object内。下标的引用要比用indexOf搜索数组快很多。

但是内存占用方面应该第二种方法比较多,因为多了一个hash表。这就是所谓的空间换时间。


Array.prototype.unique2 = function(){
    var n = {},r=[]; //n为hash表,r为临时数组
    for(var i = 0; i < this.length; i++) //遍历当前数组
      {
         if (!n[this[i]]) {
           //如果hash表中没有当前项
             n[this[i]] = true; //存入hash表
             r.push(this[i]); //把当前数组的当前项push到临时数组里面
          }
      }
      return r;
}

console.log([1,2,3,'1','2','3',1,2].unique2())

思路3:

先把数组排序,然后比较相邻的两个值。 排序的时候用的JS原生的sort方法,JS引擎内部应该是用的快速排序。


Array.prototype.unique3 = function(){
    this.sort();
    var re=[this[0]];
    for(var i = 1; i < this.length; i++){
       if( this[i] !== re[re.length-1]){
           re.push(this[i]);
       }
    }
    return re;
}

思路4:

利用数组的indexOf方法。此方法的目的是寻找存入参数在数组中第一次出现的位置。

很显然,js引擎在实现这个方法的时候会遍历数组直到找到目标为止。所以此函数会浪费掉很多时间。


Array.prototype.unique4 = function(){
    var n = [this[0]]; //结果数组
    for(var i = 1; i < this.length; i++) //从第二项开始遍历
    { //如果当前数组的第i项在当前数组中第一次出现的位置不是i,
      //那么表示第i项是重复的,忽略掉。否则存入结果数组
      if (this.indexOf(this[i]) == i) n.push(this[i]);
    }

    return n;
}

//*  =============================== 更新新的es5方法 ===========================*//

1、借助栈的方法

let unique = arr => {
  let res = [];
  while (arr.length > 0) {
    let item = arr.shift();
    res.indexOf(item) === -1 && res.push(item);
  }
  return res;
};
console.log(unique([1, 4, 5, 6, 6, 7, 7, 999, 999, 8, 7, 999]));

2、Array from + Set 方法
不考虑兼容性,这种去重的方法代码最少。但是无法去掉“{}”空对象


function unique (arr) { 
	return Array.from(new Set(arr)) 
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
//[1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {}, {}]

3、Set 方法
这种去重的方法代码最少。但是set的一个最大的特点就是数据不重复


function unique(arr) {
  return [...new Set(arr)];
}

console.log(unique([1, 2, 2, 3, 4, 4]));

4、indexOf() + lastIndexOf()方法

let unique = arr => {
  let result = [];
  arr.forEach((item, index) => {
    if (arr.indexOf(item, index) === arr.lastIndexOf(item) && result.indexOf(item) === -1) {
      result.push(item);
    }
  });
  return result;
};

let arr = [1, 4, 5, 6, 6, 7, 7, 999, 999, 8, 7, 999];
console.log(unique(arr));

5、includes()方法

function unique(arr) {
  let res = [];
  arr.forEach(item => {
    if (!res.includes(item)) {
      res.push(item);
    }
  });
  return res;
}

console.log(unique([1, 2, 3, 4, 3, 3, 3, 7, 6, 6, 9, 9]));

6、hasOwnProperty

function unique(arr) {
	 var obj = {}; 
	 return arr.filter(function(item, index, arr){
	 	 return obj.hasOwnProperty(typeof item + item) ? false : (obj[typeof item + item] = true) 
	 }) 
} 
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}]; 
console.log(unique(arr)) 
//[1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {…}] 
//所有 的都去重了

7、利用reduce+includes

function unique(arr){
	 return arr.reduce((prev,cur) => prev.includes(cur) ? prev : [...prev,cur], []);
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}]; 
console.log(unique(arr)); 
// [1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {…}, {…}]

8、利用Map数据结构去重

function arrayNonRepeatfy(arr) {
	 let map = new Map(); 
	 let array = new Array(); // 数组用于返回结果 
	 for (let i = 0; i < arr.length; i++) {
	 	 if(map .has(arr[i])) {// 如果有该key值 
	 	 	 map .set(arr[i], true); 
	 	 } 
	 	 else {
	 	 	 map .set(arr[i], false); // 如果没有该key值 
	 	 	 array .push(arr[i]);
	 	  } 
	 }
	 return array ; 
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}]; 
console.log(unique(arr)) 
//[1, "a", "true", true, 15, false, 1, {…}, null, NaN, NaN, "NaN", 0, "a", {…}, undefined]

9、利用filter


function unique(arr) { 
	return arr.filter(function(item, index, arr) {
	 	//当前元素,在原始数组中的第一个索引 === 当前索引值,否则返回当前元素 
	 	return arr.indexOf(item, 0) === index; 
	 }); 
}
 var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}]; console.log(unique(arr)) 
 //[1, "true", true, 15, false, undefined, null, "NaN", 0, "a", {…}, {…}]

10、简化map+filter 去重


function unique(arr) { 
	const res = new Map(); //定义常量 res,值为⼀个Map对象实例
    //返回arr数组过滤后的结果,结果为⼀个数组 过滤条件是,如果res中没有某个键如no,如没键值就直接arr,就设置这个键的值为1    
     return arr.filter((arr) => !res.has(arr.no) && res.set(arr.no, 1)) 
}
 var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}]; console.log(unique(arr)) 
 //[1, "true", true, 15, false, undefined, null, "NaN", 0, "a", {…}, {…}]

数组去重的一些总结

@色少9年前

03/13
23:40
前端技能

;!function(){}的一些理解

;!function(){…}

目的是防止在minify合并行的时候由于前一行缺少分号导致错误 比如:

alert(1)
!function(){…}

合并后变成 alert(1)!function… 就syntax error了 加一个分号就好了;

=================================================================

延展一下:
!function(){alert('aaa')}()        // true
+function(){alert('aaa')}()        // NaN
-function(){alert('aaa')}()        // NaN
~function(){alert('aaa')}()        // -1

因为这样的代码混淆了函数声明和函数调用,以这种方式声明的函数

1
a

,就应该以

1
a();

的方式调用。

但是括号则不同,它将一个函数声明转化成了一个表达式,解析器不再以函数声明的方式处理函数a,而是作为一个函数表达式处理,也因此只有在程序执行到函数a时它才能被访问。所以,任何消除函数声明和函数表达式间歧义的方法,都可以被解析器正确识别。所以,赋值,逻辑,甚至是逗号,各种操作符都可以告诉解析器,这个不是函数声明,它是个函数表达式。并且,对函数一元运算可以算的上是消除歧义最快的方式,感叹号只是其中之一,如果不在乎返回值,这些一元运算都是有效的; 

测试下()是性能最优。因此常用于 (function(){})();也叫:自执行函数表达式;

参考:

http://swordair.com/function-and-exclamation-mark/

http://suqing.iteye.com/blog/1981591

《Javascript高级程序设计(第3版)》 7.3 模仿块级作用域

;!function(){}的一些理解