字符串加密

字符串加密

加密操作时常也是会用到,比如用在自动登录的情况下,将账号密码进行加密后保存,打开网页自动执行登录操作的过程

那么问题来了,如果对账号密码进行保存而保持一定的安全性呢,这时候就需要对字符串进行加密操作了,当然如何解密也只有自己知道

首先来看四个函数

function setCookie(c_name,value,expiredays){
  var exdate=new Date()
  exdate.setDate(exdate.getDate()+expiredays)
  document.cookie=c_name+ "=" +escape(value)+((expiredays==null) ? "" : ";expires="+exdate.toGMTString())
}

function getCookie(c_name){
  if (document.cookie.length>0){
    c_start=document.cookie.indexOf(c_name + "=")
    if (c_start!=-1){ 
      c_start=c_start + c_name.length+1 
      c_end=document.cookie.indexOf(";",c_start)
      if (c_end==-1) c_end=document.cookie.length
        return unescape(document.cookie.substring(c_start,c_end))
      } 
    }
  return ""
}
2.js对字符串进行加密和解密
function compileStr(code){ //对字符串进行加密
  code = String(code)       
  var c=''; 
  for(var i=0;i<code.length;i++){        
    c+=String.fromCharCode(code.charCodeAt(i)+998); //+998 对数字编码加密,当然可以是任意自己下想要的数字
  }     
 return escape(c);   
}  

//字符串进行解密   
function uncompileStr(code){        
 code=unescape(code);       
 var c='';        
 for(var i=0;i<code.length;i++){        
  c+=String.fromCharCode(code.charCodeAt(i)-998);        
 }        
 return c;   
}  

经过上面的加密操作后就可以正常存入cookie或者localStroage了,解密后请直接使用

翻页列表(记住当前位置)

为了更好的用户体验,列表页大都选择分页加载的方式,现在的问题是什么呢,就是从分页列表第三页点击进图列表详情页,按浏览器返回键后回到列表页,如果停留在之前的位置😆,说到底就是能监听路由变化,不改变原页面。

实现思路一

1.项目使用browserHistory路由默认

如果项目是合并到后端部署需要后端同步配置,在后端项目路由最后一行,match *path 直接渲染前端html文件,
比如 ruby项目->config/routes.rb文件最后一行需添加
     match "*path", to: "pc#index", via: :all

2.列表点击路由不做跳转,push哈希值

onClick=function(){
    browserHistory.push('/#123')
    fetch({xxx:123})...
}
此刻详情页以模态框的形式显示

3.监听浏览器 哈希值 的改变

如果存在hash则请求详情数据,同时显示模态框,否则影藏模态框

window.onhashchange = function(location) {
    console.log(location,'------')
    if(location.xxx){
        fetch(...)
    }
}

实现思路二(参考上儿科普教育)

路由页面:

<Route path='/KePuJY' getComponent={KePuJY} onEnter={()=>this.setTitleAndShare('科普教育')}>
    <Route path=':id' getComponent={ArticleDetail} onEnter={()=>this.setTitleAndShare('文章详情')}></Route>
</Route>

文章列表页:

import { browserHistory} from 'react-router'
componentDidMount(){
    browserHistory.listen((location)=>{
        if(location.action == 'POP'){
            this.setState({display:'none'})
        }else{
            this.setState({display:'block'})
        }
    })
}
render(){
    return (
        <div className="xx">
            ul>li*10
            <div style={{display:this.state.display,width:'100%',height:'100%'}}>
                {this.props.children}
            </div>
        </div>
    );
}

详情页:

.ArticleDetail{
    position: absolute;
    top: 0;
    left: 0;
    z-index: 99;
}

就先酱紫吧😯

温馨提示:

1. iscroll内部盒子元素padding会导致高度计算不准确

2. getBoundingClientRect在苹果手机首次加载的时候不正确,滚动后才正确

时间工具-moment

moment.js一般用来处理时间的工具,实用性还是很广的喔,这里整理了部分功能,应该够我用的了😯,详情请参见官方网站:moment.js

<script src='https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.20.1/moment.min.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.20.1/locale/zh-cn.js'></script>

这里引入了cdn moment.min.js汉化包

页面输出

当前时间moment() = Fri Jan 05 2018 16:54:35 GMT+0800


初始格式化moment().format() = 2018-01-05T16:54:35+08:00


设置时间moment(“2010-3-5 15:10:3:123”) = Fri Mar 05 2010 15:10:03 GMT+0800


.date()是获取,.date(-7)是设置时间


moment().date() = 5
moment().date(-7).format(“YYYY-MM-DD”) = 2017-12-24


取值 = Fri Mar 05 2010 15:10:03 GMT+0800


moment().get(‘year’);

moment().get('month');  // 0 to 11

moment().get('date');

moment().get('hour');

moment().get('minute');

moment().get('second');

moment().get('millisecond');

moment().set(“year”, 2013) == moment().year(2013)


设置相对时间:moment().add(-3, “d”)=Tue Jan 02 2018 16:54:35 GMT+0800

Key    Shorthand    
years  y
quarters Q
months    M
weeks    w
days    d
hours    h
minutes    m
seconds    s
milliseconds    ms

格式化format()moment().format(“[today] dddd”) = today 星期五
moment().format(“YYYY-MM-DD”) = 2018-01-05
moment(2015/12/2).format(“YYYY-MM-DD”) = 2015-12-02


转化为时间戳:moment()-0 = 1515142475927


相对时间:moment(“2017-9-10”).fromNow() = 4 个月前
相对时间:moment(“2017-9-10”).fromNow(true); = 4 个月


日历时间:moment().calendar() = 今天16:54
日历时间:moment(“2018-1-4”).calendar(); = 昨天00:00


时差 (毫秒) :
var a = moment(“2017-1-20”);
var b = moment(“2017-1-19”);
a.diff(b) =
86400000
日历时间:a.diff(b, “days”); = 1


时间比较:moment(‘2010-10-20’).isBefore(‘2010-10-21’)
isBefore、isSame、isAfter、isBetween


script提供js支持

<script >
    window.onload=function(){
        moment.locale('zh-cn')  //设置中文  需要引入带locale的js文件生效

        document.write('<span style="color:red">当前时间moment()</span> = ',moment())
        document.write('<hr/>')

        document.write('<span style="color:red">初始格式化moment().format()</span> = ', moment().format())
        document.write('<hr/>')

        document.write('<span style="color:red">设置时间moment("2010-3-5 15:10:3:123")</span> = ', moment('2010-3-5 15:10:3:123'))
        document.write('<hr/>')
        document.write('<br/>')

        document.write('<span style="color:red">.date()是获取,.date(-7)是设置时间</span>')
        document.write('<hr/>')
        document.write('<span style="color:red">moment().date()</span> = ',moment().date())
        document.write('<br/>')
        document.write('<span style="color:red">moment().date(-7).format("YYYY-MM-DD")</span> = ',moment().date(-7).format('YYYY-MM-DD'))
        document.write('<hr/>')
        document.write('<br/>')

        document.write('<span style="color:red">取值</span> = ', moment('2010-3-5 15:10:3:123'))
        document.write('<hr/>')
        document.write(`moment().get('year');<br/>
                                        moment().get('month');  // 0 to 11<br/>
                                        moment().get('date');<br/>
                                        moment().get('hour');<br/>
                                        moment().get('minute');<br/>
                                        moment().get('second');<br/>
                                        moment().get('millisecond');<br/>`
        )
        document.write('<hr/>')

        document.write('<span style="color:red">moment().set("year", 2013) == moment().year(2013)</span>')
        document.write('<br/>')
        document.write('...')
        document.write('<hr/>')

        document.write('<span style="color:red">设置相对时间:moment().add(-3, "d")=</span>',moment().add(-3, 'd'))
        document.write('<br/>')
        document.write(`
            Key    Shorthand<br/>
            years    y<br/>
            quarters    Q<br/>
            months    M<br/>
            weeks    w<br/>
            days    d<br/>
            hours    h<br/>
            minutes    m<br/>
            seconds    s<br/>
            milliseconds    ms<br/>
        `)
        document.write('<hr/>')
        document.write('<br/>')


        document.write('<span style="color:red">格式化format()</span>')
        document.write('<span style="color:red">moment().format("[today] dddd") = </span>', moment().format('[today] dddd'))
        document.write('<br/>')
        document.write('<span style="color:red">moment().format("YYYY-MM-DD") = </span>', moment().format("YYYY-MM-DD"))
        document.write('<br/>')
        document.write('<span style="color:red">moment(2015/12/2).format("YYYY-MM-DD") = </span>', moment('2015/12/2').format("YYYY-MM-DD"))
        document.write('<br/>')
        document.write('<hr/>')

        document.write('<span style="color:red">转化为时间戳:moment()-0 = </span>', moment()-0)
        document.write('<br/>')
        document.write('<hr/>')


        document.write('<span style="color:red">相对时间:moment("2017-9-10").fromNow() = </span>', moment("2017-9-10").fromNow())
        document.write('<br/>')
        document.write('<span style="color:red">相对时间:moment("2017-9-10").fromNow(true); = </span>', moment("2017-9-10").fromNow(true))
        document.write('<hr/>')

        document.write('<span style="color:red">日历时间:moment().calendar() = </span>', moment().calendar())
        document.write('<br/>')
        document.write('<span style="color:red">日历时间:moment("2018-1-4").calendar(); = </span>', moment("2018-1-4").calendar())
        document.write('<hr/>')

        var a = moment("2017-1-20");
        var b = moment("2017-1-19");
        document.write(`
            <span style="color:red">时差 (毫秒) :
            var a = moment("2017-1-20");
            var b = moment("2017-1-19");
            a.diff(b) = </span>`, a.diff(b))
        document.write('<br/>')
        document.write('<span style="color:red">日历时间:a.diff(b, "days"); = </span>', a.diff(b, 'days'))
        document.write('<br/>')
        document.write('<hr/>')

        document.write(`<span style="color:red">时间比较:moment('2010-10-20').isBefore('2010-10-21')</span>`)
        document.write('<br/>')
        document.write(`<span style="color:red">isBefore、isSame、isAfter、isBetween</span>`)
        document.write('<br/>')
        document.write('<hr/>')

    }
</script>

常用的方法直接输出到页面,看的已经很直白明显了喔

移动端适配

移动端要适配什么?先来一个震文图

flexible 是 2014年5月诞生的,国内的大前端对于移动端的经验还不算成熟,而手机淘宝能有这种思路,已经是非常厉害的一个方案!

现在出现了很多新的机型,适配方案需要稍加更改,修改如下

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
;(function(win) {
var doc = win.document;
var docEl = doc.documentElement;
var metaEl = doc.querySelector('meta[name="viewport"]');
var dpr = 0;
var scale = 0;
var tid;
var flexible = {};
if (metaEl) {
console.warn('将根据已有的meta标签来设置缩放比例');
var match = metaEl.getAttribute('content').match(/initial\-scale=([\d\.]+)/);
if (match) {
scale = parseFloat(match[1]);
dpr = parseInt(1 / scale);
}
}
if (!dpr && !scale) {
var isAndroid = win.navigator.appVersion.match(/android/gi);
var isIPhone = win.navigator.appVersion.match(/iphone/gi);
var devicePixelRatio = win.devicePixelRatio;
if (isIPhone) {
// iOS下,对于2和3的屏,用2倍的方案,其余的用1倍方案
if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) {
dpr = 3;
} else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)){
dpr = 2;
} else {
dpr = 1;
}
} else {
// 其他设备下,仍旧使用1倍的方案
dpr = 1;
}
scale = 1 / dpr;
}
docEl.setAttribute('data-dpr', dpr);
if (!metaEl) {
metaEl = doc.createElement('meta');
metaEl.setAttribute('name', 'viewport');
metaEl.setAttribute('content', 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
if (docEl.firstElementChild) {
docEl.firstElementChild.appendChild(metaEl);
} else {
var wrap = doc.createElement('div');
wrap.appendChild(metaEl);
doc.write(wrap.innerHTML);
}
}
function refreshRem(){
var width = docEl && docEl.clientWidth || doc.body.clientWidth || win.innerWidth;
var rem = width / 10;
docEl.style.fontSize = rem + 'px';
flexible.rem = win.rem = rem;
}
win.addEventListener('resize', function() {
clearTimeout(tid);
tid = setTimeout(refreshRem, 300);
}, false);
win.addEventListener('pageshow', function(e) {
if (e.persisted) {
clearTimeout(tid);
tid = setTimeout(refreshRem, 300);
}
}, false);

if (doc.readyState === 'complete') {
doc.body.style.fontSize = 12 * dpr + 'px';
} else {
doc.addEventListener('DOMContentLoaded', function(e) {
doc.body.style.fontSize = 12 * dpr + 'px';
}, false);
}
refreshRem();
flexible.rem2px = function(d) {
var val = parseFloat(d) * this.rem;
if (typeof d === 'string' && d.match(/rem$/)) {
val += 'px';
}
return val;
}
flexible.px2rem = function(d) {
var val = parseFloat(d) / this.rem;
if (typeof d === 'string' && d.match(/px$/)) {
val += 'rem';
}
return val;
}
win.flexible = flexible;
})(window);

其中有几点需要说明一下

1.对安卓手机统一修改dpr=1


详情请参见:
Meta viewport

2.去掉了refreshRem函数中对宽度的限制

部分高分辨率安卓机会显得字体很小等问题

1
2
3
if (width / dpr > 540) {
width = 540 * dpr;
}

3.替换getBoundingClientRect

此属性还是存在兼容性问题,部分安卓机获取的值是空

适配的关键是设置根目录font-size的大小,至于设置设置的viewportscale是为了保证屏幕尺寸和设计稿尺寸一致,便于开发调试,设置缩放比scale后会直接影响到获取的页面的宽高尺寸

input[type=file]图片上传

图片上传到处都能遇到,如何子定义一个截图上传的组件?那就需要了解下面几点基础知识啦~

开发一个本地上传图片控件,需要支持三种上传方式:

  1. 选择本地图片上传
  2. 拖曳图片上传
  3. 截图上传

Action1.选择本地图片上传

<input id='img-input' type='file' accept='image/*.jpg'/><br/>
<img src="" alt="" width='200' id='img1'>
var ipt = document.getElementById('img-input')
// 置空input value
function clearImgInputValue () {
  ipt.value = ''
}
ipt.onchange = function(e){
  console.log('choose a image')
  // 获取File对象
  const file = e.target.files[0]
  let size = file.size
  // if (!file.type.match('image.*')) {
  //   this.clearImgInputValue()
  //   return;
  // }
  // if (size > 5000) {
  //   this.clearImgInputValue()
  //   return;
  // }
  const reader = new FileReader()  // FileReader
  // 将图片转换为base64
  reader.readAsDataURL(file)
  reader.onload = (arg) => {
    // 获取到base64图片内容
    document.querySelector('#img1').src = arg.target.result
    clearImgInputValue()
  }
}

Action2.拖曳图片上传

<div id='drop-zone' style="width:100px;height:100px">拖拽到这里放手~</div>
<img src="" alt="" width='200' id='img2'>
function handleDragOver(event){
  event.stopPropagation()
  event.preventDefault()
  event.dataTransfer.dropEffect = 'copy'
}

// 必须阻止dragenter和dragover事件的默认行为,这样才能触发 drop 事件
function handleFileSelect(event){
  event.stopPropagation()
  event.preventDefault()

  const files = event.dataTransfer.files // 文件对象
  const file = files[0]
  const size = file.size
  const type = file.type

  // if (!type.match('image.*')) {
  //   return;
  // }
  // if (size > 5000) {
  //   return;
  // }
  const reader = new FileReader()
  // 将图片转换为base64
  reader.readAsDataURL(file)
  reader.onload = (arg) => {
    // 获取到base64图片内容
    document.querySelector('#img2').src = arg.target.result
  }
}

const dropZone = document.getElementById('drop-zone');
dropZone.addEventListener('dragover', handleDragOver, false);
dropZone.addEventListener('drop', handleFileSelect, false);

Action3.截图上传

document.addEventListener('paste', function(e){
  console.log('paste a image')
  const clipboard = e.clipboardData
  // 有无内容
  if (!clipboard.items || !clipboard.items.length) {
    return;
  }
  let item = clipboard.items[0]
  if (item.kind === 'file' && item.type.match('image.*')) {
      // 获取图片文件
      let imgFile = item.getAsFile()
      // if (imgFile.size > maxSize) {
      //   return;
      // }
      const reader = new FileReader()
      // 将图片转换为base64
      reader.readAsDataURL(imgFile)
      reader.onload = (arg) => {
        // 获取到base64图片内容
        const fileStream = arg.target.result
      }
  } else {
    console.log('...')
  }
}, false)

CSS路径运动与裁剪

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<style>
    .run { 
        width:50px;
        height: 50px;
        background: green;
        display: inline-block; /* 块级元素才可以*/
        animation: move 3s linear infinite;
      offset-path: path("M10,80 q100,120 120,20 q140,-50 160,0");
      offset-rotation:auto -30deg;  /*默认是auto  可以组合属性*/
      transform-origin:top right;
      /*clip-path: inset(0px 0px 0px 0px);*/   /* 裁剪1-1  */

      /*clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%); /*按路径裁剪  完整的闭合区间*/  /* 裁剪2-1  */

      /* SVG的路径 */
      clip-path: url(#c1);  /* 裁剪3-1  */
    }
    @keyframes move {
      100% { 
          offset-distance: 100%;   /* 运动的距离 从path 0 ~ 100% */

          /*clip-path: inset(10px 10px 20px 30px);*/  /* 裁剪1-2  */
          /*用外向内裁剪 上右下左各裁剪 10px 10px 20px 30px的宽度*/

          /*clip-path: polygon(0 100%, 50% 0, 100% 100%, 0 0);  诡异的蝴蝶图形 */   
          /*clip-path: polygon(0 0, 100% 50%, 100% 50%, 0 100%);  /* 三角形闭合区间 */  /* 裁剪2-2  */

      }
    }
    svg{
        position: absolute;
    }
</style>
<body>

        <svg width="280" height="150" viewBox="0 0 280 150">
            <!-- 画一条可以看见的路径 -->
        <path d="M10,80 q100,120 120,20 q140,-50 160,0" stroke="#cd0000" stroke-width="2" fill="none" />

        <!-- 定义一个clippath -->
        <defs>
            <clipPath id="c1">
                <path d="M43.4,50h-7.7c-2.5,0-4.7-2-4.9-4.5l0,0l0-8.1c0,0,0,0,0-0.1s0,0,0-0.1l0,0c0-1.2-1-2.1-2.1-2.2h-7.3
                              c-1.2,0.1-2.1,1.1-2.1,2.2l0,0c0,0,0,0,0,0.1s0,0,0,0.1l0,0.1v8.1l0,0C19,48,16.9,50,14.3,50H6.6c-2.3,0-4.1-1.8-4.1-4.1V27.6
                              c-0.2-0.2-0.3-0.4-0.5-0.6c0-0.1-0.1-0.2-0.2-0.2l0,0c-0.6-1-0.9-1.9-0.9-2.9c0-1.4,0.6-2.7,1.7-3.7l0.3-0.3L21,1.7
                              c1-1,2.4-1.6,3.9-1.7l0,0l0,0c1.5,0,2.9,0.6,3.9,1.7l18.1,18.1l0.3,0.3c1.1,0.9,1.7,2.3,1.7,3.7c0,1-0.3,1.9-0.9,2.9l0,0
                              c-0.1,0.1-0.1,0.2-0.2,0.2c-0.2,0.2-0.3,0.4-0.4,0.6v18.3C47.5,48.1,45.6,50,43.4,50L43.4,50z M33.3,45.3c0.1,1.2,1.1,2.1,2.3"/>
            </clipPath>
            </defs>
    </svg>

    <div class='run'></div>

</body>
</html>

原型链的创建与继承

JavaScript的原型链和Java的Class区别就在,它没有“Class”的概念,所有对象都是实例,所谓继承关系不过是把一个对象的原型指向另一个对象而已。

万物皆对象

对象都具有属性__proto__

在JS里,万物皆对象。方法(Function)是对象,方法的原型(Function.prototype)是对象。因此,它们都会具有对象共有的特点。即:对象具有属性__proto__,可称为隐式原型,一个对象的隐式原型指向构造该对象的构造函数的原型,这也保证了实例能够访问在构造函数原型中定义的属性和方法

一切对象都可看作是用构造函数生成,
如果一个对象没有构造函数,那么他的构造函数就是:
Function Object(){}

Function对象

方法对象同时具有(_proto_ 和 prototype)

方法这个特殊的对象,除了和其他对象一样有上述_proto_属性之外,还有自己特有的属性——原型属性(prototype),这个属性是一个指针,指向一个对象,这个对象的用途就是包含所有实例共享的属性和方法(我们把这个对象叫做原型对象)。原型对象也有一个属性,叫做constructor,这个属性包含了一个指针,指回原构造函数。

instanceof

instanceof 操作符的内部实现机制和隐式原型、显式原型有直接的关系。instanceof的左值一般是一个对象,右值一般是一个构造函数,用来判断左值是否是右值的实例

对象值访问

JavaScript对每个创建的对象都会设置一个原型(_proto_),指向它的原型对象_proto_ = {}。
当我们用obj.xxx访问一个对象的属性时,JavaScript引擎先在当前对象上查找该属性,如果没有找到,就到其原型对象上找,如果还没有找到,就一直上溯到Object.prototype对象,最后,如果还没有找到,就只能返回undefined。

创建原型继承

  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    1.改变_proto_指向
    var Student = {
    name: 'Robot',
    height: 1.2,
    run: function () {
    console.log(this.name + ' is running...');
    }
    };

    var xiaoming = {
    name: '小明'
    };

    xiaoming.__proto__ = Student;
    1
    2
    xiaoming.name; // '小明'
    xiaoming.run(); // 小明 is running...

    请注意,上述代码仅用于演示目的。在编写JavaScript代码时,不要直接用obj.__proto__去改变一个对象的原型,并且,低版本的IE也无法使用__proto__Object.create()方法可以传入一个原型对象,并创建一个基于该原型的新对象(原对象属性不会继承)

  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    // 原型对象:
    var Student = {
    name: 'Robot',
    height: 1.2,
    run: function () {
    console.log(this.name + ' is running...');
    }
    };

    function createStudent(name) {
    // 基于Student原型创建一个新对象:
    var s = Object.create(Student);
    // 初始化新对象:
    s.name = name;
    return s;
    }

    var xiaoming = createStudent('小明');
    xiaoming.run(); // 小明 is running...
    xiaoming.__proto__ === Student; // true ******* *******

JSON

几乎所有编程语言都有解析JSON的库,而在JavaScript中,我们可以直接使用JSON,因为JavaScript内置了JSON的解析。为了统一解析,JSON的字符串规定必须用双引号””,Object的键也必须用双引号””。

JSON常用的无非就2个方法:

序列化(stringify)

让我们先把小明这个对象序列化成JSON格式的字符串:

var xiaoming = {
      name: '小明',
      age: 14,
      gender: true,
      height: 1.65,
      grade: null,
      'middle-school': '\"W3C\" Middle School',
      skills: ['JavaScript', 'Java', 'Python', 'Lisp']
  };
  JSON.stringify(xiaoming); 
//'{
      "name":"小明","age":14,"gender":true,
      "height":1.65,"grade":null,
      "middle-school":"\"W3C\" Middle School",
      "skills":["JavaScript","Java","Python","Lisp"]
      }'

要输出得好看一些,可以加上参数,按缩进输出:

JSON.stringify(xiaoming, null, '  ');

结果:

{
  "name": "小明",
  "age": 14,
  "gender": true,
  "height": 1.65,
  "grade": null,
  "middle-school": "\"W3C\" Middle School",
  "skills": [
    "JavaScript",
    "Java",
    "Python",
    "Lisp"
  ]
}

第二个参数用于控制如何筛选对象的键值,如果我们只想输出指定的属性,可以传入Array:

JSON.stringify(xiaoming, ['name', 'skills'], '  ');

结果:

{
  "name": "小明",
  "skills": [
    "JavaScript",
    "Java",
    "Python",
    "Lisp"
  ]
}

还可以传入一个函数,这样对象的每个键值对都会被函数先处理:

function convert(key, value) {
    if (typeof value === 'string') {
        return value.toUpperCase();
    }
    return value;
}

JSON.stringify(xiaoming, convert, ‘ ‘);
上面的代码把所有属性值都变成大写:

{
  "name": "小明",
  "age": 14,
  "gender": true,
  "height": 1.65,
  "grade": null,
  "middle-school": "\"W3C\" MIDDLE SCHOOL",
  "skills": [
    "JAVASCRIPT",
    "JAVA",
    "PYTHON",
    "LISP"
  ]
}

如果我们还想要精确控制如何序列化小明,可以给xiaoming定义一个toJSON()的方法,直接返回JSON应该序列化的数据:

var xiaoming = {
    name: '小明',
    age: 14,
    gender: true,
    height: 1.65,
    grade: null,
    'middle-school': '\"W3C\" Middle School',
    skills: ['JavaScript', 'Java', 'Python', 'Lisp'],
    toJSON: function () {
        return { // 只输出name和age,并且改变了key:
            'Name': this.name,
            'Age': this.age
        };
    }
};
JSON.stringify(xiaoming); // '{"Name":"小明","Age":14}'

反序列化(parse)

拿到一个JSON格式的字符串,我们直接用JSON.parse()把它变成一个JavaScript对象:

JSON.parse('[1,2,3,true]'); // [1, 2, 3, true]
JSON.parse('{"name":"小明","age":14}'); // Object {name: '小明', age: 14}
JSON.parse('true'); // true
JSON.parse('123.45'); // 123.45

JSON.parse()还可以接收一个函数,用来转换解析出的属性:

JSON.parse('{"name":"小明","age":14}', function (key, value) {
    // 把number * 2:
    if (key === 'name') {
        return value + '同学';
    }
    return value;
}); // Object {name: '小明同学', age: 14}

JS运算符和进制转换

>>表示是带符号的右移

按照二进制把数字右移指定数位,高位如符号位为正补零,符号位负补一,低位直接移除

8>>1  // 4
-8>>1 // -4

>>>表示无符号的右移

按照二进制把数字右移指定数位,高位直接补零,低位移除!

8>>>1 // 4
-8>>>1 // 2147483644

应用示例

取中间数(自动减去小数位)
4>>1  //2
5>>1  //2

~符号

按位非运算符,简单的理解就是改变运算数的符号减去1(舍去小数),当然,这是只是简单的理解能转换成number类型的数据.总结:方向取反,奇偶取反,数值向0靠近1个count

typeof var(数据类型) !== ”number”的类型来说,进行运算时,会尝试转化成32位整形数据,如果无法转换成整形数据,就转换为NaN;继而转为-1

~3.7 == -4    // -3.7 - 1 = -4.7  --> -4 
~-2.9 == 1    // 2.9 -1 = 1.9 -->1
~-1 == 0 
~~2.9 == 2
~~ -2.9 == -2
----------------------------------------------------
以下不能转为Number类型全为 -1
~{} == -1
~[] == -1
~NaN ==-1
~function(){} == -1

应用示例

var str = 'abc'

before:

alert(str.indexOf('d') != -1 ? '有字母d' : '没有字母d')
----------------------------------------------------
after:

alert(~str.indexOf('d') ? '有字母d' : '没有字母d')

&运算符

二进制安位与:
4 & 2           3 & 2
0100            0011
0010            0010
----            ----
0000  ->0       0010  ->2

应用示例

JS中的主要用来判断奇偶:
3 & 1 -> 1
4 & 1 -> 0    

进制转换

<其他进制 转为 十进制>
parseInt("11", 2); // 3    2进制转10进制 
parseInt("77", 8); // 63   8进制转10进制
parseInt("af", 16); //175  16进制转10进制

<十进制 转为 其他进制>
(152).toString(2) // "10011000"; 先用括号将152转换“包”成一个对象.
或者如下写法:
152..toString(2) // 这里第一个点将152转换成float类型的小数,第二个点是引出对象方法;
152..toString(16) // "98" : 十进制转16进制

箭头函数

箭头函数要实现类似纯函数的效果,必须剔除外部状态。所以当你定义一个箭头函数,在普通函数里常见的 thisargumentscaller 是统统没有的。

箭头函数没有 this

箭头函数没有 this ,那下面的代码明显可以取到 this 啊:

function foo() {
  let a = 1
  let b = () => console.log(this.a)

  b()
}
foo()  // 1

以上箭头函数中的 this 其实是父级作用域中的 this ,即函数 foo 的 this 。箭头函数引用了父级的变量,构成了一个闭包。以上代码等价于:

function foo() {
  let a = 1
  let self = this
  let b = () => console.log(self.a)
  b()
}
foo()  // 1

一个经常犯的错误是使用箭头函数定义对象的方法,如:

let a = {
  foo: 1,
  bar: () => console.log(this.foo)
}
a.bar()  //undefined

以上代码中,箭头函数中的 this 并不是指向 a 这个对象。对象 a 并不能构成一个作用域,所以再往上到达全局作用域, this 就指向全局作用域。


另一个错误是在原型上使用箭头函数,如:

function A() {
  this.foo = 1
}
A.prototype.bar = () => console.log(this.foo)
let a = new A()
a.bar()  //undefined

同样,箭头函数中的 this 不是指向 A ,而是根据变量查找规则回溯到了全局作用域。同样,使用普通函数就不存在问题

箭头函数没有arguments

function foo() {
  return () => console.log(arguments[0])
}
foo(1, 2)(3, 4)  // 1

上例中如果箭头函数有 arguments ,就应该输出的是3而不是1。

通过以上说明,我们可以看出,箭头函数除了传入的参数之外,真的是什么都没有!如果你在箭头函数引用了 this 、 arguments 或者参数之外的变量,那它们一定不是箭头函数本身包含的,而是从父级作用域继承的。

那么问题来了,什么情况下该使用箭头函数

  • 箭头函数适合于无复杂逻辑或者无副作用的纯函数场景下,例如用在 map 、 reduce 、 filter 的回调函数定义中;
  • 不要在最外层定义箭头函数,因为在函数内部操作 this 会很容易污染全局作用域。最起码在箭头函数外部包一层普通函数,将 this 控制在可见的范围内;