mamba


  • 首页

  • 关于

  • 标签

  • 分类

  • 归档

  • 日程表

JavaScript类型判断

发表于 2019-05-03

typeof、prototype、instanceof、constructor方法比较

typeof

我们最常见的是typeof,通常我们会看到

1
console.log(typeof('mamba'))  //string

这种写法,但是typeof可是一个一元运算符,就跟加减乘除一样

1
console.log(typeof 'mamba')  //string

《JavaScript权威指南》中对typeof的介绍:

typeof 是一元操作符,放在其单个操作数的前面,操作数可以是任意类型。返回值为表示操作数类型的一个字符串。

在 ES6 前,JavaScript 共六种数据类型,分别是:

1
Undefined、Null、Boolean、Number、String、Object

然而当我们使用 typeof 对这些数据类型的值进行操作的时候,返回的结果却不是一一对应,分别是:

1
undefined、object、boolean、number、string、object

注意以上都是小写的字符串。Null 和 Object 类型都返回了 object 字符串。
尽管不能一一对应,但是typeof却能检测出函数类型:

1
2
function a() {}
console.log(typeof a) //function

所以 typeof 能检测出六种类型的值,但是,除此之外 Object 下还有很多细分的类型呐,如 Array、Function、Date、RegExp、Error 等。

例如:

1
2
3
4
var date = new Date();
var error = new Error();
console.log(typeof date); // object
console.log(typeof error); // object

返回的都是 object ,所以就要用Object.prototype.toString来检测了

Object.prototype.toString(通用但很繁琐)

当 toString 方法被调用的时候,下面的步骤会被执行:

  • 如果 this 值是 undefined,就返回 [object Undefined]
  • 如果 this 的值是 null,就返回 [object Null]
  • 让 O 成为 ToObject(this) 的结果
  • 让 class 成为 O 的内部属性 [[Class]] 的值
  • 最后返回由 “[object “ 和 class 和 “]” 三个部分组成的字符串

通过规范,我们至少知道了调用 Object.prototype.toString 会返回一个由 “[object “ 和 class 和 “]” 组成的字符串,而 class 是要判断的对象的内部属性。

让我们写个 demo:

1
2
3
4
5
console.log(Object.prototype.toString.call(undefined)) // [object Undefined]
console.log(Object.prototype.toString.call(null)) // [object Null]

var date = new Date();
console.log(Object.prototype.toString.call(date)) // [object Date]

1
2
3
4
5
6
7
8
9
10
11
var number = 1;            // [object Number]
var string = '123'; // [object String]
var boolean = true; // [object Boolean]
var und = undefined; // [object Undefined]
var nul = null; // [object Null]
var obj = {a: 1} // [object Object]
var array = [1, 2, 3]; // [object Date]
var date = new Date(); // [object Date]
var error = new Error(); // [object Error]
var reg = /a/g; // [object RegExp]
var func = function a(){}; // [object Function]

除以上11种还有

1
2
3
4
5
6
7
console.log(Object.prototype.toString.call(Math)); // [object Math]
console.log(Object.prototype.toString.call(JSON)); // [object JSON]

function a() {
console.log(Object.prototype.toString.call(arguments)); // [object Arguments]
}
a();

既然有了 Object.prototype.toString 这个神器!那就让我们写个 type 函数帮助我们以后识别各种类型的值吧!
写一个 type 函数能检测各种类型的值,如果是基本类型,就使用 typeof,引用类型就使用 toString。此外鉴于 typeof 的结果是小写,我也希望所有的结果都是小写。

  • 考虑兼容性

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    var class2type = {};

    // 生成class2type映射
    "Boolean Number String Function Array Date RegExp Object Error".split(" ").map(function(item, index) {
    class2type["[object " + item + "]"] = item.toLowerCase();
    })

    function type(obj) {
    // 一箭双雕
    if (obj == null) {
    return obj + "";
    }
    return typeof obj === "object" || typeof obj === "function" ?
    class2type[Object.prototype.toString.call(obj)] || "object" :
    typeof obj;
    }
  • 不考虑兼容性

    1
    2
    3
    function type(val){
    return Object.prototype.toString.call(val).split(' ')[1].split(']')[0].toLowerCase();
    }

isFunction

有了 type 函数后,我们可以对常用的判断直接封装,比如 isFunction:

1
2
3
function isFunction(obj) {
return type(obj) === "function";
}

数组

jQuery 判断数组类型,旧版本是通过判断 Array.isArray 方法是否存在,如果存在就使用该方法,不存在就使用 type 函数。

1
2
3
var isArray = Array.isArray || function( obj ) {
return type(obj) === "array";
}

待续···

1
2
3
4
5
6
var a = "iamstring.";
var b = 222;
var c= [1,2,3];
var d = new Date();
var e = function(){alert(111);};
var f = function(){this.name="22";}

判断已知对象类型的方法:instanceof

1
2
3
4
alert(c instanceof Array) ---------------> true
alert(d instanceof Date) ---------------> true
alert(f instanceof Function) ------------> true
alert(f instanceof function) ------------> false
  • 注意:instanceof后面一定要是对象类型,并且大小写不能错,该方法适合一些条件选择或分支。

    根据对象的constructor判断:constructor
    1
    2
    3
    alert(c.constructor === Array) ----------> true
    alert(d.constructor === Date) -----------> true
    alert(e.constructor === Function) -------> true
  • 注意: constructor 在类继承时会出错

例子:

function A(){};
function B(){};
A.prototype = new B(); //A继承自B
var aObj = new A();
alert(aobj.constructor === B) -----------> true;
alert(aobj.constructor === A) -----------> false;
而instanceof方法不会出现该问题,对象直接继承和间接继承的都会报true:
alert(aobj instanceof B) ----------------> true;
alert(aobj instanceof B) ----------------> true;
言归正传,解决construtor的问题通常是让对象的constructor手动指向自己:
aobj.constructor = A; //将自己的类赋值给对象的constructor属性
alert(aobj.constructor === A) -----------> true;
alert(aobj.constructor === B) -----------> false; //基类不会报true了;

JavaScript数组

发表于 2019-05-03

数组去重

双层循坏
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var array = [1, 1, '1', '1'];

function unique(array) {
//res用来存储结果
var res = []
for (var i = 0; i < array.length; i++) {
for (var j = 0; j < res.length; i++) {
if (array[i] === res[j]) {
break
}
}
// 如果array[i]是唯一的,那么执行完循环,j等于resLen
if (j === res.length) {
res.push(array[i])
}
}
return res
}

console.log(unique(array)); // [1, "1"]
indexOf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
var array = [1, 1, '1'];

function unique(array) {
var res = [];
for (var i = 0, len = array.length; i < len; i++) {
var current = array[i];
if (res.indexOf(current) === -1) {
res.push(current)
}
}
return res;
}

console.log(unique(array));
排序后去重

试想我们先将要去重的数组使用 sort 方法排序后,相同的值就会被排在一起,然后我们就可以只判断当前元素与上一个元素是否相同,相同就说明重复,不相同就添加进 res,让我们写个 demo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var array = [1, 1, '1'];

function unique(array) {
var res = [];
var sortedArray = array.concat().sort();
var seen;
for (var i = 0, len = sortedArray.length; i < len; i++) {
// 如果是第一个元素或者相邻的元素不相同
if (!i || seen !== sortedArray[i]) {
res.push(sortedArray[i])
}
seen = sortedArray[i];
}
return res;
}

console.log(unique(array));

如果我们对一个已经排好序的数组去重,这种方法效率肯定高于使用 indexOf。

待续···

JavaScript深浅拷贝

发表于 2019-05-03

数组的浅拷贝

如果是数组,我们可以利用数组的一些方法比如:slice、concat 返回一个新数组的特性来实现拷贝。

比如:

1
2
3
4
5
6
7
8
var arr = ['old', 1, true, null, undefined];

var new_arr = arr.concat();

new_arr[0] = 'new';

console.log(arr) // ["old", 1, true, null, undefined]
console.log(new_arr) // ["new", 1, true, null, undefined]

用 slice 可以这样做:

1
var new_arr = arr.slice();

但是如果数组嵌套了对象或者数组的话,比如:

1
2
3
4
5
6
7
8
9
var arr = [{old: 'old'}, ['old']];

var new_arr = arr.concat();

arr[0].old = 'new';
arr[1][0] = 'new';

console.log(arr) // [{old: 'new'}, ['new']]
console.log(new_arr) // [{old: 'new'}, ['new']]

我们会发现,无论是新数组还是旧数组都发生了变化,也就是说使用 concat 方法,克隆的并不彻底。

  • 如果数组元素是基本类型,就会拷贝一份,互不影响(slice和concat如果拷贝基本类型元素的数组是深拷贝,否则是浅拷贝),而如果是对象或者数组,就会只拷贝对象和数组的引用,这样我们无论在新旧数组进行了修改,两者都会发生变化。我们把这种复制引用的拷贝方法称之为浅拷贝,
  • 与之对应的就是深拷贝,深拷贝就是指完全的拷贝一个对象,即使嵌套了对象,两者也相互分离,修改一个对象的属性,也不会影响另一个。

所以我们可以看出使用 concat 和 slice 是一种浅拷贝。(对于数组和对象来说)

数组的深拷贝

那如何深拷贝一个数组呢?这里介绍一个技巧,不仅适用于数组还适用于对象!那就是:

1
2
3
4
5
var arr = ['old', 1, true, ['old1', 'old2'], {old: 1}]

var new_arr = JSON.parse( JSON.stringify(arr) );

console.log(new_arr);

是一个简单粗暴的好方法,就是有一个问题,不能拷贝函数,

浅拷贝的实现

以上三个方法 concat、slice、JSON.stringify 都算是技巧类,可以根据实际项目情况选择使用,

接下来我们思考下如何实现一个对象或者数组的浅拷贝。
想一想,好像很简单,遍历对象,然后把属性和属性值都放在一个新的对象不就好了~
嗯,就是这么简单,注意几个小点就可以了:

1
2
3
4
5
6
7
8
9
10
11
12
13
var shallowCopy = function(obj) {
// 只拷贝对象
if (typeof obj !== 'object') return;
// 根据obj的类型判断是新建一个数组还是对象
var newObj = obj instanceof Array ? [] : {};
// 遍历obj,并且判断是obj的属性才拷贝
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = obj[key];
}
}
return newObj;
}

深拷贝的实现

那如何实现一个深拷贝呢?说起来也好简单,我们在拷贝的时候判断一下属性值的类型,如果是对象,我们递归调用深拷贝函数不就好了~

1
2
3
4
5
6
7
8
9
10
var deepCopy = function(obj) {
if (typeof obj !== 'object') return;
var newObj = obj instanceof Array ? [] : {};
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key];
}
}
return newObj;
}

性能问题

尽管使用深拷贝会完全的克隆一个新对象,不会产生副作用,但是深拷贝因为使用递归,性能会不如浅拷贝,在开发中,还是要根据实际情况进行选择。

购物车三级联动分析

发表于 2019-03-30

在做YZ项目时购物车三级联动没分析清楚,写下这篇来梳理业务逻辑

分析

  • 购物车列表(cartList)异步获取
  • 层级关系为 单个商品选中——店铺选中——全选,三个层级相互依赖,相互联系
  • 控制选中状态核心在于 单个商品及店铺的状态 所以在获取数据之后应当立即对shop.checked和good.checked进行初始化(由于Vue的响应式原理,需要先处理数据后赋值才能使checked动态响应)
  • 自下而上:从对单个商品的状态判断店铺和全选的状态,从店铺的状态判断全选的状态
  • 自上而下:从全选的状态直接控制店铺和单个商品的状态,从店铺的状态直接控制单个商品的状态
  • 全选比较特殊,控制所有,又被牵连其中,使用计算属性get和set

JavaScript垃圾回收

发表于 2019-03-29

垃圾回收算法

1.标记清除算法

标记阶段:从根集合出发,将所有活动对象及其子对象打上标记
清除阶段:遍历堆,将非活动对象(未打上标记)的连接到空闲链表上

优点

实现简单, 容易和其他算法组合

缺点

碎片化, 会导致无数小分块散落在堆的各处
分配速度不理想,每次分配都需要遍历空闲列表找到足够大的分块
与写时复制技术不兼容,因为每次都会在活动对象上打上标记

2.引用计数 Reference Counting

引用计数,就是记录每个对象被引用的次数,每次新建对象、赋值引用和删除引用的同时更新计数器,如果计数器值为0则直接回收内存。 很明显,引用计数最大的优势是暂停时间短

优点

可即刻回收垃圾
最大暂停时间短
没有必要沿指针查找, 不要和标记-清除算法一样沿着根集合开始查找

缺点

计数器的增减处理繁重
计数器需要占用很多位
实现繁琐复杂, 每个赋值操作都得替换成引用更新操作
循环引用无法回收

3.分代回收

出发点:大部分对象生成后马上就变成垃圾,很少有对象能活的很久
新生代 = 生成空间 + 2 * 幸存区 复制算法

老年代 标记-清除算法

对象在生成空间创建,当生成空间满之后进行 minor gc,将活动对象复制到第一个幸存区,并增加其“年龄” age,当这个幸存区满之后再将此次生成空间和这个幸存区的活动对象复制到另一个幸存区,如此反复,当活动对象的 age 达到一定次数后将其移动到老年代; 当老年代满的时候就用标记-清除或标记-压缩算法进行major gc
吞吐量得到改善, 分代垃圾回收花费的时间是GC复制算法的四分之一;但是如果部分程序新生成对象存活很久的话分代回收会适得其反

JavaScript异步

发表于 2019-03-29

Vue组件之间的通信

发表于 2019-03-29

父子组件:

  • 父对子:prop传递数据
  • 子对父:$emit触发自定义事件

如果父组件传递给子组件的是一个引用类型的数据,在子组件内部对数据源进行修改的时候,父组件会同步修改。
为了避免同步修改,需要对其进行深复制

1
2
3
4
5
6
props:['obj'],
data(){
return {
myObj: JSON.parse(JSON.stringify(this.obj))
}
}

爷孙组件:

使用两次 v-on 通过爷爷爸爸通信,爸爸儿子通信实现爷孙通信

任意组件:

  • 使用 eventBus = new Vue() 来通信,eventBus.$on 和 eventBus.$emit 是主要API
  • 使用 Vuex 通信

v-clock解决页面加载的闪烁

发表于 2019-03-29

v-cloak

这个指令保持在元素上直到关联实例结束编译。和 CSS 规则如
[v-cloak] { display: none
一起用时,这个指令可以隐藏未编译的 Mustache 标签直到实例准备完毕。

  • 在html挂载点加上 v-cloak,就可以解决页面加载时的闪烁(编译完成时再进行页面渲染)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <div id="app" v-cloak>
    {{msg}}
    </div>

    <style>
    [v-cloak] {
    display: none;
    }
    </style>

    v-cloak失效的问题:

如果把[v-cloak]样式写在@import加载的文件里很有可能失效,光是加载这个文件就需要时间,解决方法最好是 写成内联style或者link引入。

Vue文档学习小结

发表于 2019-03-29

leanCloud数据存储(JavaScript)

发表于 2019-03-29

利用leanCloud进行数据存储

1.安装与引用

yarn add leancloud-storage –save

2.初始化函数

var APP_ID = ‘‘;
var APP_KEY = ‘‘;

AV.init({
appId: APP_ID,
appKey: APP_KEY
});

3.创建class

4.批量获取

5.更新

6.删除

12345

张哲

48 日志
GitHub E-Mail
© 2019 张哲
由 Hexo 强力驱动
|
主题 — NexT.Pisces v5.1.4