Some tips in Jan,2018

TODO

  • Object.defineproperty()
  • margin垂直重叠
  • 字的基线
  • 深拷贝与浅拷贝
  • 未声明的变量会自动升级为全局变量的原因
  • HTTPS与HTTP
  • > ~ +
  • 使用rem设置元素宽高在chrome中的问题
  • IOT 中pull-right/经纬度/地区输入
  • 小题
1
2
3
4
5
6
7
8
9
10
var obj1 = {
age: 20,
getAge: function() { return this.age; }
}
var obj2 = { age: 30 }

var getAge = obj1.getAge;
obj2.getAge = getAge;
console.log(obj1.getAge());
console.log(obj2.getAge());

Object.defineProperty()

1
Object.defineProperty(obj, prop, descriptor)

该方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。与常见的直接使用.[]不同,它能通过descriptor参数定义属性的更多设定。

obj

要在其上定义属性的对象。

prop

字符串,要定义或修改的属性的名称。

descriptor/属性描述符

对象里目前存在的属性描述符有两种主要形式:数据描述符和存取描述符。数据描述符是一个具有值的属性,该值可能是可写的,也可能不是可写的。访问器描述符是由getter-setter函数对描述的属性。描述符必须是这两种形式之一;不能同时是两者。

/ configurable enumerable value writable get set
数据描述符 yes y y y no n
存取描述符 y y n n y y

configurable

设置属性描述符descriptor是否能被修改,默认为flase;如果尝试去更改configurable:false属性描述符内的属性则会报错

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var a = {
age: 1
};
Object.defineProperty(a, 'name' , {
configurable: false,
enumerable: false,
value: 'obj'
});
Object.defineProperty(a, 'name' , {
configurable: false,
enumerable: false,
value: 'name'
});
// "error"
// "TypeError: Cannot redefine property: name

enumerable

设置属性是否能被枚举,默认为flase,不能被枚举;

1
2
3
4
5
6
7
8
9
10
11
12
13
var a = {
age: 1
};
Object.defineProperty(a, 'name' , {
value: 'obj'
});
for(var key in a){
console.log(key + ': ' + a[key]);
}
// "age: 1"
console.log(Object.keys(a));
// ["age"]
// 默认情况下'name'无法被枚举出来
1
2
3
4
5
6
7
8
9
var a = {
age: 1
};
Object.defineProperty(a, 'name' , {
enumerable: true,
value: 'obj'
});
console.log(Object.keys(a));
// ["age", "name"]

value

默认为undefined

writable

仅当该属性的writabletrue时,value才能被赋值运算符改变。默认为 false

这样一道题:
1
2
3
4
5
6
7
8
9
10
11
12
/**
Question : please create a tool to generate Sequence
Expected to be used like:
var sequence1 = new Sequence();
sequence1.next() --> return 1;
sequence1.next() --> return 2;

in another module:
var sequence2 = new Sequence();
sequence2.next() --> 3;
sequence2.next() --> 4;
**/

实例化构造函数时,新的实例都会刷新变量,因此使用通常的方法无法实现效果。

使用Object.defineProperty,并设置writable: true解决方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function Sequence() {
this.next = function() {
Sequence.number++;
console.log(Sequence.number);
return Sequence.number;
}
}

Object.defineProperty(Sequence, "number", {
value: 0,
writable: true,
enumerable: false,
configurable: false,
});

get

给属性提供 getter 的方法

set

给属性提供 setter的方法,该方法将接受唯一参数,并将该参数的新值分配给该属性。

数据描述符

1
2
3
4
5
6
7
8
var obj = {};

Object.defineProperty(obj, "demo", {
configurable: true,
enumerable: true,
writable: true,
value: 666 //数据描述符
});

存取描述符

1
2
3
4
5
6
7
8
9
10
11
12
13
var temp;
var obj = {};

Object.defineProperty(obj, 'demo', {
configurable: true,
enumerable: true,
get: function(){
return temp;
},
set: function(new){
temp = new;
}
});

参考:MDN web docs

margin的垂直重叠

两个或多个相邻的普通流中的块元素垂直方向上的 margin 会折叠

两个或多个

需要两个或两个以上的元素参与的行为,且折叠是相互行为,不存在 A 和 B 折叠,B 没有和 A 折叠的现象。

相邻

兄弟同级相邻

两个元素时兄弟,且二者之间没有其他元素:

1
2
3
4
<div>
<div style='margin-bottom:20px'></div>
<div style='margin-top:10px'></div>
</div>

此时二者的间距为20px,产生重叠;

若兄弟间有其他元素,则不会产生重叠,二者间距30px;

1
2
3
4
5
<div>
<div style='margin-bottom:20px'></div>
<span>第三者</span>
<div style='margin-top:10px'></div>
</div>

父子相邻

父子之间的margin-top相邻或margin-bottom相邻;

1
2
3
4
5
<div style='margin-bottom:20px'>
<div>111</div>
<div style='margin-bottom:10px'></div>
</div>
<div>NEXT</div>

此时父级的margin-bottom与子级的margin-bottom相邻,产生重叠,对于父级外界而言,只取margin-bottom最大值,即20px;

其他相邻

其实只要满足上下相邻,中间没有其他元素皆可。

普通流

相邻的浮动、inline-blockabsolute定位的元素垂直方向的margin不会发生重叠;

块元素

内联元素不可设置宽高和margin

垂直方向

只有在垂直方向才会发生重叠,水平方向不会。

margin重叠有什么意义

看起来有点奇怪的规则,其实有其现实意义。比如在我们使用p元素排版段落时,p元素默认外边距是16px,上下两个p元素不会产生中间间隔32px的现象;

1
2
<p>1111</p>
<p>2222</p>

如何使margin垂直方向不重叠

  • 使用padding代替;
  • 设置父级元素overflow: hidden/auto;
  • 设置二者之一的元素display: inline-block
  • 设置元素浮动或者绝对定位;

注:并不是所有的BFC都可以使margin垂直方向不重叠,设置overflow、display、浮动和绝对定位只是BFC的子集;

字的基线与vertical-align

字的基线

CSDN文章

mark

vertical-align

定义

默认基线对齐baseline

1
2
3
4
5
<div>
<span>aghGi</span>
<img class="img-sm" src="https://simmzl.cn/v2.0/img/icon.png" width=100px;>
<img src="https://simmzl.cn/v2.0/img/icon.png" width=200px;>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
div {
margin: 20px;
background: #ffd800;
width: 600px;
height: 100px;
display: inline-block;
font-size: 0;
}

span {
background: rgba(0, 0, 0, 0.1);
color: #fff;
font-size: 36px;
}

默认基线对齐:

mark

Top

把元素的顶端与其他行中最高元素的顶端对齐

1
2
3
.img-sm {
vertical-align: top;
}

效果:

mark

Bottom

把元素的顶端与其他行中最低的元素的顶端对齐

1
2
3
span {
vertical-align: bottom;
}

效果:

mark

Middle

把元素的中部和父元素字体的中线对齐

1
2
3
4
5
6
<div>
Father-gjly
<span>aghGi</span>
<img class="img-sm" src="https://simmzl.cn/v2.0/img/icon.png" width=100px;>
<img src="https://simmzl.cn/v2.0/img/icon.png" width=200px;>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
div {
margin: 20px;
background: #ffd800;
width: 600px;
height: 200px;
display: inline-block;
font-size: 16px;;
}

span {
background: rgba(0, 0, 0, 0.1);
color: #fff;
font-size: 36px;
}

.img-sm {
vertical-align: middle;
}

效果:

mark

text-top & text-bottom

把元素的顶端与父元素字体的顶端对齐 & 把元素的底端与父元素字体的底端对齐

text-top:

mark

text-bottom:

mark

百分比或者长度

  • %: 使用 “line-height” 属性的百分比值来排列此元素。允许使用负值,需要设置line-height;
  • 长度:正负px;

以上两种只会让元素在父元素内上下移动,向上最多到父元素顶部,向下则可以出父元素

1
2
3
4
5

/* 基于默认基线处上移33px和img-sm居中对齐(不精确) */
span {
vertical-align: 33px;
}

mark

深拷贝与浅拷贝

讲的很好:
深拷贝与浅拷贝的区别,实现深拷贝的几种方法

总结一下:

基本数据类型:number,string,boolean,null,undefined五类,无需深拷贝,直接存在栈内
引用数据类型:(Object类),有常规名值对的无序对象{a:1},数组[1,2,3],以及函数等。存储在栈中的是堆中的地址,浅拷贝只拷贝栈中地址,故需要深拷贝。

实现深拷贝:

  • 递归赋值所有层级属性
  • 使用JSON.stringify()JSON.prase()

像使用slice() / concat() / Object.assign()等只能深拷贝一层,不是真正的深拷贝。

未声明的变量会自动升级为全局变量的原因

js 严格模式下,为什么不能给未声明变量赋值

拓展:
10分钟理解JS引擎的执行机制

JavaScript运行原理解析

~ > +

1
2
3
4
5
6
7
8
9
10
11
12
13
<body>
<div>
<h1>1</h1>
<p>pppppp</p>
<h1>2</h1>
<h1>3</h1>
</div>
<h1>4</h1>
</body>

p~h1 {
color: yellow;
}

p~h1选择和p元素父级相同,在p元素之后的元素,所以coloryellow的是 2 3

1
2
3
4
5
6
7
8
9
10
11
12
13
<div class="foo">
<h1>1</h1>
<h1>1</h1>
<h1>1</h1>
<h1>1</h1>
<div>
<h1>2</h1>
</div>
</div>

.foo > h1 {
color: red;
}

.foo > h1选择的是.foo内的所有元素,但只选择一代,所有只有 1 才显示为红色;

1
2
3
4
5
6
7
<div class="foo">11</div>
<div class="boo">22</div>
<div class="boo">33</div>

.foo + .boo {
color: red;
}

.foo + .boo选择的是.foo之后紧邻且同级.boo元素。所以红色的为2233并不会变红。

使用rem设置元素宽高在chrome中的问题

rem是相对于html根节点的长度单位。在使用rem替代px时,常设置:html: { font-size: 62.5% }, 16px为默认字体大小,1rem = 62.5% * 16px = 10px,方便计算,所以24px可以表示为2.4rem,即2.4 * 62.5% * 16px = 24px
但在chrome中由于浏览器默认最小字体为12px,所以导致1rem = 12px,width:10rem渲染出来是width:120px,报道出了偏差…

解决

为了避免出现10px被设置为12px的问题,可将 62.5% 改为 625%,则1rem = 625% * 16px = 100px:

1
2
3
html: {
font-size: 625%;
}

100px可表示为1rem, 24px可表示为.24rem

小题

1
2
3
4
5
6
7
8
9
10
var obj1 = {
age: 20,
getAge: function() { return this.age; }
}
var obj2 = { age: 30 }

var getAge = obj1.getAge;
obj2.getAge = getAge;
console.log(obj1.getAge()); // 20
console.log(obj2.getAge()); // 30

这涉及到this的引用

  • this是函数内部的一个特殊对象(或this引用)–它引用的是函数据以执行的环境对象。(来源于JavaScript高级程序设计)

  • this引用是一种在JavaScript的代码中随时都可以使用的只读变量。 this引用,引用(指向)的是一个对象,它有着会根据代码上下文语境自动改变其引用对象的特性

  • JavaScript是动态语言,this关键字在执行的时候才能确定是谁,所以this永远指向调用者,即对“调用对象”的引用。简单点说就是 调用的方法属于哪个对象,this就指向那个对象。根据函数调用方式的不同,this可以 指向全局对象,当前对象,或其他任意对象。this四个调用模式

因此,this只有在被调用时才确定指向,console.log(obj2.getAge()) 的结果是obj2.age,即30。

拓展:

动态语言:是指程序在运行时可以改变其结构:新的函数可以被引进,已有的函数可以被删除等在结构上的变化,类型的检查是在运行时做的,优点为方便阅读,清晰明了,缺点为不方便调试。如JavaScript;

静态语言:静态类型语言的类型判断是在运行前判断(如编译阶段),比如C#、java。