超人来了
阅读全文
20230603
阅读全文
阿里云

阿里云的一个坑

今天我将一个node的后端项目部署到阿里云上,数据库正常启动, node项目也使用pm2正常跑起来了,nginx也正常,单是我前段老是访问不了后端,经过一天的排查,发现是我node后端占用的3000这个端口号被阿里云的防火墙截了!!!

如果你要是用某个端口号,一点更要记得在在防火墙加上你要使用的端口号!!!(我使用的是轻量级的服务器)

阅读全文
this指向

this的绑定规则

默认绑定

this默认指向window

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
// 1. 全局环境下的this指向window
console.log(this); // window
// 2. 函数独立调用时,函数内部的this也指向window
function fn() {
console.log(this);
}
fn(); // window
// 3.被嵌套的函数独立调用时,this默认指向window
let a = 1;
let obj = {
a: 2,
foo: function () {
// 函数当做对象的方法来调用,this指向obj
let self = this;
function test() {
console.log(self.a); // 2
console.log(this); //window
}
test();
},
};
obj.foo();
// 4.IIFE 自执行函数
var b = 10;
function foo1() {
console.log(this); // obj1
// (function test () {
// console.log(this); // window
// console.log(this.b); // 10
// })();
(function test (self) {
console.log(self.b); // 20
})(this);
}
var obj1 = {
b: 20,
foo1: foo1
};
obj1.foo1();

(function () {
console.log(this); // window
})();
// 5. 闭包中的this默认指向window
var obj2 = {
c:40,
foo2: function () {
var d = this.c
return function test () {
console.log('闭包中的this:' + this); // window
return d
}
}
}
var f = obj2.foo2()
console.log(f()); // 40

隐式绑定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function foo () {
console.log(this.a);
}
let obj = {
a: 1,
foo: foo,
obj1: {
a:2,
foo: foo
}
}
// foo()函数的直接对象(.前面的)是obj,this指向了这个直接对象
obj.foo() // 1
// foo()函数的直接对象(.前面的)是obj1,this指向了这个直接对象
obj.obj1.foo() // 2

隐式丢失

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
// 隐式丢失:指被隐式绑定的函数丢失了绑定对象 从而默认绑定到了window
// 1. 函数别名导致隐式丢失
function foo () {
console.log(this.a);
}
var a = 0
let obj = {
a: 1,
foo
}
/**
* 把obj.foo赋值给别名bar,造成隐式丢失情况,bar与obj毫无关系
*/
var bar = obj.foo
bar() // 0
// 以上代码等价于
// var bar = function foo () {....}

// 2. 参数传递
var b = 11
function foo1 () {
console.log(this.b);
}
function bar1 (fn) {
fn()
}
var obj1 = {
b: 12,
foo1:foo1
}
// 把obj1.foo1当做参数传递到bar1函数中,有隐式的函数赋值(fn=obj1.foo1),即会出现函数丢失的情况
bar1(obj1.foo1) // 11
/**
* 以上代码相当于:
var b = 11
function bar1 (fn) {
fn()
}
bar1(function foo1(){
// this指向window
console.log(this.a);
})
*/

// 3.内置函数, setTimeout()/setInterval()中的第一个参数的回调函数中的this默认指向了window
// setTimeout(function(){
// console.log(this); // window
// }, 1000);
var c = 22
function foo3 () {
console.log(this.c); // 22
}
var obj3 = {
c: 33,
foo3: foo3
}
setTimeout(obj3.foo3, 1000)

// 4.间接调用
function foo4 () {
console.log(this.dd);
}
var dd = 33
var obj4 = {
dd: 44,
foo4: foo4
}
var p = {dd: 55}
obj4.foo4() // 44
// 将obj4.foo4函数对象赋值给了p.foo5函数,然后立即执行,相当于仅仅是foo4函数的立即调用,this指向window
!(p.foo5 = obj4.foo4)() // 33
// 将obj.foo4赋值给了p.foo6函数, p.foo()函数再执行,其实是属于p对象的方法执行,this指向当前p对象
p.foo6 = obj4.foo4
p.foo6() // 55

// 5. 其他情况 -- this指向window
var ee = 88
var obj5 = {
ee: 77,
foo8: foo8
}
function foo8 () {
console.log(this.ee);
}
!(obj5.foo8 = obj5.foo8)() // 88 -- this指向window
!(false || obj5.foo8)() // 88 -- this指向window
!(1, obj5.foo8)() // 88 -- this指向window

显示绑定

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
// 显示绑定:使用bind(), apply(), call()把对象绑定到this上

// 硬绑定:是显示绑定的一种变形,使得this不能再被改变
var a = 0
function foo () {
console.log(this.a);
}
var obj = {
a: 2
}
var bar = function () {
foo.call(obj)
}
bar() // 2
setTimeout(bar, 1000) // 2
bar.call(window) // 2

// 数组的forEach(fn, 对象)/map() /filter() /some() /every() -- 具有显示绑定this,能改变当前回调函数中this指向

var id = 'window'
function fn (el) {
console.log(el, this.id);
}
var obj1 = {
id: 'fn'
}
~[1,2,3].forEach(fn) // 1 'window', 2 'window', 3 'window'
~[1,2,3].forEach(fn, obj1) // 1 'fn', 2 'fn', 3 'fn'

new 绑定

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
// new 绑定
function fn () {
// 如果是new 关键字来执行函数,相当于构造函数来是实例化对象,那么内部的this指向了当前实例化的对象
console.log(this); // this指向fn(){}
}
let fn1 = new fn()
console.log(fn1); // this指向fn(){}

function fn2 () {
console.log(this); // this指向fn2(){},即this指向当前的实例对象
// 使用return关键字返回对象时,实例化后的对象的this指向return返回的对象
return {
name: 'lg'
}
}
let f = new fn2()
console.log(f); // f的this指向{name: 'lg'}

var person = {
fav: function () {
return this
}
}
console.log(person.fav()); // this指向person对象
var p = new person.fav()
console.log(p, p===person); // p指向fav, false

严格模式下的this指向

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 严格模式下this的指向
// 1. 独立调用的函数内部的this指向了undefined
function fn () {
'use strict'
console.log(this); // undefined
}
fn()
// 2. 在严格模式下,函数apply()和call()内部的this始终是它们的第一个参数
var color = 'blue'
function f () {
'use strict'
console.log(this); // null
console.log(this.color);
}
f.call(null) // 报错,在严格模式下,call中传入什么,this就是什么

总结

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1. 默认绑定
2. 隐式绑定
3. 显示绑定
4. new绑定
这四种绑定分别对应函数的四种调用:
- 独立调用
- 方法的调用
- 间接调用
call(), bind(), apply()
- 构造函数调用

隐式丢失
- 函数别名
- 函数做参数传递
- 内置函数
- 间接调用
阅读全文
JS数组去重

使用underscore.js中的_.uniq()方法

ES6中的Set

1
2
let arr = [1, 3, 3, 4, 9 ,5, 5, 7, 0, 5]
let result = Array.from(new Set(arr))
1
2
let arr = [1, 3, 3, 4, 9 ,5, 5, 7, 0, 5]
let result = [...new Set(arr)]

循环遍历

1
2
3
4
5
6
let result = []
arr.forEach(item => {
if (result.indexOf(item) === -1) {
result.push(item)
}
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 双重for循环
let len = arr.length
let flag;
let result = []
for (let i = 0; i < len; i++) {
flag = false
for(let j=i+1; j < len; j++) {
if (arr[i] === arr[j]) {
flag = true
}
}
if (!flag) {
result.push(arr[i])
}
}

ES6的filter函数

1
2
3
let result = arr.filter((item, index) => {
return arr.indexOf(item) === index
})

reduce函数

1
2
3
let result = arr.reduce((pre, item) => {
return pre.includes(item) ? pre : [...pre, item]
}, [])

转化为对象的键

1
2
3
4
5
let result = {}
arr.forEach((item, index) => {
return result[item] = index
})
result = Object.keys(result).map(item => ~~item)

排序方式

一旦使用排序,面试官大几率会问你排序算法

1
2
3
4
5
6
7
8
9
// 一旦使用排序,面试官大几率会问你排序算法
let result = []
let temp = arr.sort()
let len = temp.length
for (let i=0; i<len; i++) {
if (arr[i] !== arr[i+1]) {
result.push(arr[i])
}
}
阅读全文
js函数

JS中函数主要分为声明式函数和赋值式函数

声明式函数

1
2
3
function 函数名 () {
函数体..
}

对于声明式函数来说,有函数的提升,并且函数名重复的话后面的会覆盖掉前面的,其实这也引出了js中的重载问题。。。这就是后话了。。(js中没有真正意义上的重载)

1
2
3
4
5
6
7
8
9
fn()
fn()
function fn () {
console.log(1);
}
function fn () {
console.log(2);
}
// 输出两次2

以下例子会输出什么结果呢?

1
2
3
4
5
6
7
8
9
10
console.log(a)
a()
var a = 3
function a () {
console.log(6)
}
console.log(a)
a = 7
console.log(a)
a()

输出的结果应该是这样的

1
2
3
4
5
6
7
8
9
10
console.log(a) // f a () { console.log(6) }
a() // 6
var a = 3
function a () {
console.log(6)
}
console.log(a) // 3
a = 7
console.log(a) // 7
a() // 报错,此时的a是一个变量,这儿当做函数使用,肯定报错啊

那么为什么会出现以上的结果呢?

首先我们要知道函数的提升和变量的提升,理论解释百度一大堆,这里用代码例子说明一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 在上面的例子中定义了一个变量a,一个函数a
// 变量提升
var a
// 函数提升
function a () {
console.log(6)
}
// --------------
console.log(a) // 这儿为啥不输出undefined,却输出函数a呢?
// 在js中,如果变量名与函数同名,若变量没有赋值,则函数生效,否则变量生效

a() // 这儿输出6大家都明白,就不用解释了
// .....后面的代码很简单,就不说了

对于声明式函数,还有一个很有意思的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function fn () {
console.log(1)
};
(function () {
if (false) {
function fn () {
console.log(2)
}
}
fn()
})()
/*
这个代码在不同浏览器中得到的结果不一样
1. IE6~7 => 输出2
2. 低版本的火狐 => 输出1
3. 其他浏览器 => 报错
*/

赋值式函数

赋值式函数也叫函数表达式

不会出现函数提升的现象

1
2
3
4
5
fn() // 报错
var fn = function () {
console.log(1);
}
fn() // 1

但是有一个很有趣的现象

1
2
3
4
5
6
7
8
9
10
11
12
let fn = function f (n) {
f = n
console.log(typeof f); // function
return 1
}
console.log(fn()); // 1
console.log(f()); // 报错
/*
对于f有如下的特征:
1. 只读
2. 只能在函数内部访问,外部无法访问
*/
阅读全文
圣杯布局和双飞翼布局

圣杯布局

双飞翼布局

=> 左右固定,中间自适应

圣杯布局

浮动和负margin

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
<style type='text/css'>
html,
body {
height: 100%;
overflow: hidden;
}
.container {
height: 100%;
padding: 0 200px;
}
.left,
.right {
width: 200px;
min-height: 200px;
background-color: lightblue;
}
.center {
width: 100%;
min-height: 400px;
background-color: lightsalmon;
}
.left,
.center,
.right {
float: left;
}
.left {
margin-left: -100%;
position: relative;
left: -200px;
}
.right {
margin-right: -200px;
}
</style>
</head>
<body>
<div class="container clearfix">
<div class="center"></div>
<div class="left"></div>
<div class="right"></div>
</div>
</body>

双飞翼布局

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
<style type='text/css'>
html,
body {
height: 100%;
overflow: hidden;
}

.container,
.left,
.right {
float: left;
}

.container {
width: 100%;
}

.container .center {
margin: 0 200px;
min-height: 400px;
background: lightsalmon;
}

.left,
.right {
width: 200px;
min-height: 200px;
background: lightblue;
}

.left {
margin-left: -100%;
}

.right {
margin-left: -200px;
}
</style>
</head>
<body class="clearfix">
<div class="container">
<div class="center"></div>
</div>
<div class="left"></div>
<div class="right"></div>
</body>

其他方法实现以上布局

flex

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
html,
body {
overflow: hidden;
}

.container {
display: flex;
justify-content: space-between;
height: 100%;
}

.left,
.right {
flex: 0 0 200px;
height: 200px;
background: lightblue;
}

.center {
flex: 1;
min-height: 400px;
background: lightsalmon;
}

定位

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
<style>
html,
body {
height: 100%;
overflow: hidden;
}

.container {
position: relative;
height: 100%;
}

.left,
.right {
position: absolute;
top: 0;
width: 200px;
min-height: 200px;
background: lightblue;
}

.left {
left: 0;
}

.right {
right: 0;
}

.center {
margin: 0 200px;
min-height: 400px;
background: lightsalmon;
}
</style>

calc

1
2
3
4
5
6
7
.center {
/* 兼容到IE9 */
width: calc(100% - 400px);
min-height: 400px;
background: #ffa07a;
}
......
阅读全文
Js继承

什么是面向对象?

面向对象是一种编程思想

JS本身是基于面向对象构建出来的,例如JS中有很多的内置类,向promise就是ES6中新增的一个内置类,我们可以基于new Promise来创建一个实例,来管理异步编程,我在项目中,promise也经常用,也研究过它的源码。。

我们平时使用的vue,jquery等也是基于面向对象的,它们都是类,可以通过创建她们的实例来操作他们

JS中的面向对象和其他编程语言略有不同,JS中的类和实例是基于原型和原型链机制来处理的,而且js中关于类的重载、继承也和其他语言不一样

封装

低耦合高内聚,可复用性强

多态

重载

方法名相同,形参个数或者类型不一样 => Js中不存在真正意义上的重载

JS中的重载指的是同一个方法,根据传参不同,实现出不同的效果

重写

在类的继承当中,子类可以重写父类中的方法

继承

子类继承父类中的属性和方法,目的是让子类的实例能够调用父类中的属性和方法

原型继承

让父类的属性和方法再子类实例的原型链上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function A (x) {
this.x = x
}
A.prototype.getX = function () {
console.log(this.x);
}
B.prototype = new A(200) // B的原型指向A的实例
B.prototype.constructor = B
function B (y) {
this.y = y
}
B.prototype.getY = function () {
console.log(this.y);
}
let b1 = new B(100)
console.log(b1.y); // 100
console.log(b1.x); // 200
b1.getY(); // 100
b1.getX() // 200

特点

  1. 不向其他语言一样中继承(其他语言一般是拷贝继承,也就是子类继承父类,会把父类中的属性和方法拷贝一份到子类,供子类实例调用),他是把父类的原型放到子类实例的原型链上,实例想调取这些方法是基于__proto__原型链查找机制完成的
  2. 子类可以重写父类上的方法(这样会导致父类其他的实例也受到影响)
  1. 父类中私有或者公有的属性方法,最后都会变为子类中公有的属性和方法

Call继承

​ 子类方法中把父类当作普通函数来执行,让父类中的this指向子类的实例,相当于给子类实例设置了很多私有属性或方法

  1. 特点:
    • 只能继承父类私有的属性或方法(由于把父类当作来执行,和其原型上的属性和方法无关)
    • 父类私有的变成子类私有的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function A (x) {
this.x = x
}
A.prototype.getX = function () {
console.log(this.x);
}

function B (y) {
A.call(this, 200) // b1.x = 200
this.y = y
}
B.prototype.getY = function () {
console.log(this.y);
}
let b1 = new B()
console.log(b1.x);

寄生组合继承

Call继承 + 类似于原型继承

父类私有和公有属性方法分别是子类实例的私有和公有属性和方法

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
function A (x) {
this.x = x
}
A.prototype.getX = function () {
console.log(this.x);
}

function B (y) {
A.call(this, 200) // b1.x = 200
this.y = y
}
// Object.create(obj) => 创建一个空对象,让空对象的__proto__指向obj
B.prototype = Object.create(A.prototype)
B.prototype.constructor = B;
B.prototype.getY = function () {
console.log(this.y);
}
let b1 = new B()
console.log(b1.x);
/**
若不兼容Object.create方法,可以自定义一个该方法
Object.create = function (obj) {
function Fn () {}
Fn.prototype = obj
return new Fn()
}
*/

ES6中的继承简单认识

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// ES6中的继承
class A {
constructor (x) {
this.x = x
}
getX () {
console.log(this.x);
}
}
// => B.prototype.__proto__ = A.prototy
class B extends A {
constructor (y) {
super(200) // super() => A.call(this)
this.y
}
getY () {
console.log(this.y);
}
}
let b1 = new B(100)
console.log(b1);
阅读全文
Js数据类型检测

JS数据类型检测的四种方法

  1. typeof
  2. instranceof
  3. constructor
  4. Object.prototype.toString.call()

typeof

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
/**
* typeof:用来检测数据类型的运算符
* 1. typeof [value]
* 2. @return
* 1) 首先是个字符串
* 2) 字符串中包含对应的数据类型
* "number", "object", "undefined", "function", "boolean", "symbol", "bigint"
* 3. @局限
* 1) typeof null => "object"
* 2) 不能具体区分对象数据类型的值
* typeof 数组 => "object"
* typeof {} => "object"
* typeof 正则 => "object"
* 4. @优势
* 使用方便,能快速检查undefined,string,number,boolean, function等类型
*/

function func (n, m, callback) {
/* 形参赋值默认值*/
// 1. ES6 => function func (n=0, m=0) {} // 若没有传,默认为0
// 2. typeof m==="undefined" ? m=0 : null
// 3. n = n || 0 、 m = m || 0 => 不够严谨,若用户传一个false,则也会将其赋值为0

/* 回调函数执行 */
// 1. typeof callback === "function" ? callback() : null
// 2. callback && callback() ==> 传递的为真即执行,不一定是一个函数

}

instanceof && constructor

instanceof

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
/**
* instanceof: 本意是用来检测实例是否隶属于某个类的运算符,我们基于这样的方式也可以用来做某些数据类型的检测,例如数组、正则等
* @return
* true/false
* @局限性
* 1. 不能处理基本数据类型值
* 2. 只要在当前实例的原型链(__proto__)中出现过的类检测结果都是true(可能会手动修改原型链的指向:example.__proto__或者 原型继承)
*/
// ----------------------------------------------------
function func () {
// arguments 类数组(其原型链直接到Object,没有到Array)
console.log(arguments instanceof Array); // false

arguments.__proto__ = Array.prototype
console.log(arguments instanceof Array); // true
}
func()
// -----------------------------------------------------
// let s = 11
// console.log(s instanceof Number); // false
// let arr =[],
// reg = /^$/,
// obj = {};
// console.log(arr instanceof Array); // true
// console.log(reg instanceof Array); // false
// console.log(reg instanceof Object); // true
// console.log(reg instanceof RegExp); // true
// console.log(obj instanceof Array); // false
// -----------------------------------------------------
/**
* 创建数值的两种方式(不管那种方法创建都是Number类的实例,因为可以使用Number原型上的方法)
* 1. 字面量 => let n = 12 => 不能使用instanceof检测
* 2. 构造函数 => let m = new Number('13') => typeof m 的值为"object"、 m instarnceof Number为true
*/

constructor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* constructor: 构造函数
* @原理:在类的原型上一般都会带有constructor,存储当前类本身,我们也是利用这一点,获取实例的constructor值,验证是否为所属类,从而进行数据类型检测
* @局限性
* 1. constructor属性太容易被修改
*/
let n = 12,
arr = []
console.log(n.constructor === Number); // true
console.log(arr.constructor === Array); // true
console.log(arr.constructor === Object); // false

arr.constructor = 111 // => 设置私有属性,每次查找会先看自己有没有,没有再去看原型有没有
console.log(arr.constructor === Array); // false
console.log(arr.constructor === Number); // false
console.log(arr.constructor === 111); // true
Func.prototype = {} // 这样原型上就没有constructor属性了(重构了)

Object.prototype.toString.call([value])

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* Object.prototype.toString.call([value]):调用Object原型上的toString方法,让方法执行的时候,方法中的this是要检测的数据类型,从而获取到数据类型所属类的详细信息
* @返回信息的模板 => "[object 所属类]" 例如"[object Array]"
* 在所有的数据类型类中,他们的原型上都有toString方法,除了Object.prototype.toString不是把数据值转换为字符串,其余都转换为字符串
* 可以检测所有的数据类型
* "[object Undefined]" "[object BigInt]" "[object Array]" "[object Boolean]" "[object Null]" "[object String]"
* "[object Number]" "[object RegExp]" "[object Symbol]" "[object Function]" "[object Object]" "[object Date]"
* "[object Set]" "[object Math]" ....
*/

/*let obj1 = {},
obj2 = {
name : "lg"
};
*/
// console.log([11, 12].toString()); // "11, 12"
// console.log(/^\d+$/.toString()); // "/^\d+$/"
// console.log((function func () {}).toString()); // "function func () {}"
// console.log(obj1.toString()); // "[object Object]"
// console.log(obj2.toString()); // "[object Object]"
// console.log([].__proto__.__proto__.toString()); // "[object Object]"
//obj1.toString() // 基于原型链机制查找,找到Object.prototype.toString,把找到的方法执行,方法中的this执行obj1,方法内部把this(obj1)的所属类信息输出

自定义数据类型检测方法

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
// 使用ES6
<script type="text/javascript">
let _obj = {
isNumeric: "Number",
isFunction: "Function",
isString: "String",
isNull: "Null",
isUndefined: "Undefined",
isSymbol: "Symbol",
isPlainObject: "Object", // 纯粹对象
isArray: "Array",
isRegExp: "RegExp",
isDate: "Date",
isWindow: "Window",
isBoolean: "Boolean"
// .....
},
_toString = ({}).toString,
_type = {};
for (let key in _obj) {
if (!_obj.hasOwnProperty(key)) break // 不是私有属性,结束循环
let reg = new RegExp("\\[object "+ _obj[key] +"\\]")
_type[key] = function (value) {
return reg.test(_toString.call(value))
}
}
console.log(_type.isNumeric(12)); // true
console.log(_type.isNumeric("sss")); // false
console.log(_type.isFunction(Array)); // true
</script>
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
// 使用ES5 => 闭包
var _obj = {
isNumeric: "Number",
isFunction: "Function",
isString: "String",
isNull: "Null",
isUndefined: "Undefined",
isSymbol: "Symbol",
isPlainObject: "Object", // 纯粹对象
isArray: "Array",
isRegExp: "RegExp",
isDate: "Date",
isWindow: "Window",
isBoolean: "Boolean"
},
_toString = ({}).toString,
_type = {};
for (var key in _obj) {
if (!_obj.hasOwnProperty(key)) break // 不是私有属性,结束循环
_type[key] = (function () {
var reg = new RegExp("\\[object "+ _obj[key] +"\\]")
return function (value) {
return reg.test(_toString.call(value))
}
})();
}
console.log(_type.isNumeric(12)); // true
console.log(_type.isNumeric("sss")); // false
console.log(_type.isFunction(Array)); // true
阅读全文
with关键字

With

本质上是通过将一个对象的引用当做作用于来处理,将对象的属性当做作用域中的标识符来处理,从而创建一个新的词法作用域。

首先,with是JS中欺骗词法的方法之一,另一种方法是eval()

  1. with通常被当做重复引用同一对象中的多个属性的快捷方式,可以不需要重复引用对象本身
1
2
3
4
5
6
7
8
9
10
11
12
13
let obj = {
a : 3,
b : 4,
d : 7
}
with (obj) {
a = 8,
b = 9,
d = 10
}
// 使用with就相当于直接使用obj.a=8、obj.b=9、obj.d=10
console.log(obj);
// a=8, b=9, d=10
  1. 看如下代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function foo (obj) {
with (obj) {
a = 2
}
}
let o1 = {
a : 7
}
let o2 = {
b : 7
}
foo(o1)
console.log(o1.a); // 2
foo(o2)
console.log(o2.a); // undefined
console.log(a); // a

为什么会出现这种情况呢??

对于o1来说,其本身就有一个a属性,使用with关键字后,可以理解为with关键字的作用域就是o1,o1本身就有一个a属性,再对其进行了LHS引用,使a的值改变,这是显而易见的

对于o2来说,其本身没有一个a属性,使用with关键字后,with关键字的作用域就变成了o2,但是o2中没有a属性,所以o2.a就被赋值为了undefined,然后使用的LHS引用,从他的上一级作用域去去查找a,直到找到全局作用域,如果还没有找到a,就会在全局创建一个a并赋值为2(当然这是在非严格模式下的情况)

所以with会在运行时修改或创建一个新的作用域,以此来欺骗其他在书写时定义的词法作用域。

在严格模式下使用with会直接报错,严格模式下with是被禁止使用的

  1. 虽然with在某些方面极为方便,但是非常不推荐使用with关键字
    • 会影响JS引擎在编译阶段的性能优化,如果代码中由大量的with,那么运行起来会非常慢
    • 严格模式下无法使用
阅读全文