小达不刘
0%

  1. 微信小程序原生textarea的placeholder-style对rgba识别bug:(0,10-15,27,0.15)识别不出来,会设置不上颜色

  2. 滚动穿透:popup中滚动会穿透到底层

  3. 微信原生picker mode=date start,end设置后,依然会展示不在设置范围内的,只是不能选择

  4. setTimeout、setInterval实现的定时器在鸿蒙系统出现掉帧现象,目前已解决,参考高刷屏对requestAnimationFrame和setTimeout的影响

  5. 兼容性:ios和安卓底部黑条问题兼容

    1
    2
    3
    4
    5
    // **兼容iphone 下方小黑条
    @supports (bottom: constant(safe-area-inset-bottom)) or (bottom: env(safe-area-inset-bottom)) {
    padding-bottom: calc(constant(safe-area-inset-bottom)); /* 兼容 iOS < 11.2 */
    padding-bottom: calc(env(safe-area-inset-bottom)); /* 兼容 iOS >= 11.2 */
    }
  6. 在scroll-view列表中,ios底部会出现大量留白。
    解决方案: 将scroll-view中的enhanced属性置为false。

  7. 在【返回上一页】和【toast提示】这两个动作考虑好先后的时机,不然可能会toast提示不出来。
    最好在B页面,等待toast提示完再返回A页面,不要同时提示toast和返回上一页,会造成toast被返回上一页动作覆盖掉。

阅读全文 »

基本使用

1
2
arr.reduce(function(previousValue, item, index, arr){},
initialValue)
  1. callback入参:
  • previousValue:上一次调用 callbackFn 时的返回值。在第一次调用时,若指定了初始值 initialValue,其值则为 initialValue,否则为数组索引为 0 的元素 array[0]。
  • item initialValue,其值则为数组索引为 0 的元素 array[0],否则为 array[1]。
  • index:数组中正在处理的元素的索引。若指定了初始值 initialValue,则起始索引号为 0,否则从索引 1 起始。
  • arr:用于遍历的数组。
  1. initialValue 可选
    作为第一次调用 callback 函数时参数 previousValue 的值。若指定了初始值 initialValue,则 currentValue 则将使用数组第一个元素;否则 previousValue 将使用数组第一个元素,而 currentValue 将使用数组第二个元素。

  2. 返回值
    使用 “reducer” 回调函数遍历整个数组后的结果。

  3. 如果数组为空且未指定初始值 initialValue,则会抛出 TypeError。

使用场景

1 求所有数组的和

1
2
3
4
5
const arr = [1,2,3,4]
const res = arr.reduce((pre, item, index, arr)=>{
return pre + item
}, 0)
console.log(res) // 10

2 数组扁平化

阅读全文 »

原型、构造函数、实例对象的关系

构造函数能够生成实例对象,实例对象拥有构造函数里的属性和方法,并继承构造函数原型上的属性及方法。

继承方式

原型链继承

子类原型等于父类构造函数的实例

特点:
子类实例能够继成父类及父类原型中属性和方法

问题:

  1. 如果父类构造函数中有引用类型的属性,会导致通过子类创建的所有实例都共享该属性,其中一个实例修改了这个引用值,其他实例的这个属性都会同步变化,造成数据污染。
  2. 子类构造函数无法向父类构造函数传值
阅读全文 »

父子组件通信

  • 通过 props 传递
  • 通过 $emit 触发自定义事件
  • 使用 ref

祖先子孙组件通信

  • provide 与 inject
  • $attrs和 $listeners

兄弟组件

  • EventBus
  • $parent 或$root

复杂关系的组件

Vuex

阅读全文 »

绑定规则及优先级

  1. new 绑定:new foo()
  2. 显示绑定:call,apply,bind
  3. 隐式绑定:对象调用(obj.foo()),谁调用指向谁
  4. 默认绑定:默认指向window

箭头函数

箭头函数本身没有this,this指向由外层函数的作用域决定,父级this指向谁,箭头函数的this就指向谁。上面四项绑定规则对箭头函数都无效

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
var a = 0
function foo (b) {
console.log(this)
var test = () => {
console.log(this)
}
return test
}
var obj1 = {
a: 1,
foo: foo,
}
var obj2 = {
a: 2
foo: () => {
console.log(this)
}
}
obj1.foo()() // this此时指向obj1,虽然是window调用,但是默认绑定规则对箭头函数失效,箭头函数this就是父级this的指向。
var bar = foo.call(obj1) // this依然指向window, 显示绑定也无法改变this指向
obj2.foo() // this此时指向window,而不指向obj2,所以隐式调用也无效

// 箭头函数不允许作为构造函数
var foo1 = () => {
console.log(this)
}
new foo1() // 浏览器报错:Uncaught typeerror

练习题1:

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
var name = 'window'
var obj1 = {
name: '1',
fn1: function(){
console.log(this.name)
},
fn2: () => {console.log(this.name)},
fn3: function(){
return function(){
console.log(this.name)
}
},
fn4: function(){
return () => {
console.log(this.name)
}
},
}
var obj2 = {
name: '2'
}

obj1.fn1() // 1
obj1.fn1.call(obj2) // 2

obj1.fn2() // window
obj1.fn2.call(obj2) // window

obj1.fn3()() //错误 1 // 正确 window 返回的函数自调用,所以指向window
obj1.fn3().call(obj2) // 2
obj1.fn3.call(obj2)() // 错误 2 // 正确 window 返回的函数自调用,所以指向window

obj1.fn4()() // 错误window //正确 1,返回的箭头函数中this指向父级fn4,fn4的this是obj1调用,故this指向obj1, 所以答案是1
obj1.fn4().call(obj2) // 错误window //正确 1,上题返回1,call不能改变箭头函数的this,故还是1
obj1.fn4.call(obj2)() // 2,相当于fn4在obj2中了,fn4 this指向obj2,故返回的箭头函数this也指向obj2。
//想更改箭头函数的this指向,只能更改父级作用域this指向

练习题2:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function Foo() {
getName = function() {
console.log(1)
}
return this
}
Foo.getName = function(){ console.log(2) }
Foo.prototype.getName = function(){ console.log(3) }
var getName = function(){ console.log(4) }
function getName (){ console.log(5) }

Foo.getName() // 2
getName() // 4
Foo().getName() // 1
getName() // 1
// 预编译时,函数提升,执行时getNname表达式会覆盖函数getNname,所以5永远也不会打印出来了。

new Foo.getName() // 2, new的是对象上的属性,该属性是个方法(Foo.getNname = function(){ console.log(2) }),new的时候会执行里面的语句
new Foo().getName() // 3, 要这么看:(new Foo()).getName(),也就是new Foo()之后的实例对象的getName(),在实例对象上没有找到getName(),再去原型上找
new new Foo().getName() // 3,这么看:new( (new Foo()).getName() ), 最终new对象实例上的属性,实例上没有该属性,去原型上找,该属性是个方法,new时会执行里面的语句

阅读全文 »

数组遍历

for循环、while、do…while

听说速度最快,效率最高,其他不过多解释。

1
2
3
for(let i = 0; i< 5; i++) {
console.log(i)
}

for…of

遍历的是值,能正确响应return、break、continue语句。不仅可以循环遍历数组对象。还可以迭代 Array、Map、Set、String 等对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 遍历String
let str = "Hello";
for (let value of str) {
console.log(value)
}
// H e l l o

// 遍历Map
let iterable = new Map([["a", 1], ["b", 2], ["c", 3]]);
for (let entry of iterable) {
console.log(entry);
}
// ["a", 1]
// ["b", 2]
// ["c", 3]
for (let [key, value] of iterable) {
console.log(value);
}
// 1
// 2
// 3

数组内置方法:

forEach

阅读全文 »

call

实现步骤:

  1. 判断调用对象是否为函数,不是函数需要抛出异常;
  2. 判断是否传入改变this指向的第一个参数,如果没传则this默认指向window;
  3. 将this赋值给传入的第一个参数(this要指向的对象)一个属性;
  4. 将剩余参数传入这个新属性中执行;
  5. 删除新属性;
  6. 返回结果;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Function.prototype.mycall = function(ctx, ...args) {
// this此时是调用mycall的实例对象
if (typeof this !=== 'funciton') {
throw new Error('type error')
}
ctx = ctx || window
// this这个实例对象赋值给fn属性
// 优化:放置fn属性会覆盖该实例本身的fn属性
let fn = Symbol(1)
ctx[fn] = this
// 以属性方式执行
const res = ctx[fn](...args)
// 执行完删除该属性
delete ctx[fn]
return res
}

apply

apply实现与call类似,只是传参方式不同。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

Function.prototype.myapply = function(ctx, arg = []) {
// this此时是调用mycall的实例对象
if (typeof this !=== 'funciton') {
throw new Error('type error')
}
ctx = ctx || window
// this这个实例对象赋值给fn属性
// 优化:放置fn属性会覆盖该实例本身的fn属性
let fn = Symbol(1)
ctx[fn] = this
// 以属性方式执行
const res = ctx[fn](arg)
// 执行完删除该属性
delete ctx[fn]
return res
}

bind

因为bind不是立即执行的,需要手动执行,所以可以返回一个函数实现。也可以借助call或apply实现。

阅读全文 »

节流: n 秒内只运行一次,若在 n 秒内重复触发,只有一次生效
防抖: n 秒后在执行该事件,若在 n 秒内被重复触发,则重新计时

节流

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
// 时间戳
function throttled1(fn, delay = 500) {
let oldtime = Date.now()
return function (...args) {
let newtime = Date.now()
if (newtime - oldtime >= delay) {
fn.apply(null, args)
oldtime = Date.now()
}
}
}

// 定时器
function throttled2(fn, delay = 500) {
let timer = null
return function (...args) {
if (!timer) {
timer = setTimeout(() => {
fn.apply(this, args)
timer = null
}, delay);
}
}
}

// 时间戳与定时器结合更加准确
function throttled3(fn, delay) {
let timer = null
let startTime = Date.now()
return function(){
let context = this
let args = arguments
let curentTime = Date.now()
let remainTime = delay - (curentTime - startTime)
clearTimeout(timer)
if (remainTime <= 0) {
fn.apply(context, args)
startTime = Date.now()
} else (
timer = setTimeout(fn, remainTime)
)
}
}

防抖

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
// 简单版本
function debounce(fn, wait) {
let timer

return function() {
let context = this
let args = arguments
clearTimeout(timer)
timer = setTimeout(() => {
fn.apply(context, args)
}, wait)
}
}

// 防抖如果需要立即执行,可加入第三个参数用于判断,实现如下:
function debounce(func, wait, immediate) {

let timeout;

return function () {
let context = this;
let args = arguments;

if (timeout) clearTimeout(timeout); // timeout 不为null
if (immediate) {
let callNow = !timeout; // 第一次会立即执行,以后只有事件执行后才会再次触发
timeout = setTimeout(function () {
timeout = null;
}, wait)
if (callNow) {
func.apply(context, args)
}
}
else {
timeout = setTimeout(function () {
func.apply(context, args)
}, wait);
}
}
}

适用场景

  1. 防抖在连续的事件,只需触发一次回调的场景有:
  • 搜索框搜索输入。只需用户最后一次输入完,再发送请求
  • 手机号、邮箱验证输入检测
  • 窗口大小resize。只需窗口调整完成后,计算窗口大小。防止重复渲染。
  1. 节流在间隔一段时间执行一次回调的场景有:
阅读全文 »

是什么?

把用户在模板中写的类似于原生html的内容进行编译。把原生html和非原生html找出,经过一系列处理成render函数的过程称为模板编译过程。

最终目的就是:把用户所写的模板转化成供Vue实例在挂载时可调用的render函数。或者你可以这样简单的理解为:模板编译就是一台机器,给它输入模板字符串,它就输出对应的render函数。

其中有三个阶段:
1.代码解析阶段:将一堆模板字符串用正则的方式解析成抽象语法树(AST);
2.代码优化阶段:遍历AST进行静态节点的标记;
3.代码生成阶段:将AST转换成render函数;

1.代码解析阶段

HTML解析器是主线,先用HTML解析器进行解析整个模板,在解析过程中如果碰到文本内容,那就调用文本解析器来解析文本,如果碰到文本中包含过滤器那就调用过滤器解析器来解析。

HTML解析器:一边解析不同的内容一边调用对应的钩子函数生成对应的AST节点,最终完成将整个模板字符串转化成AST;
文本解析器:作用就是将HTML解析器解析得到的文本内容进行二次解析,解析文本内容中是否包含变量,如果包含变量,则将变量提取出来进行加工,为后续生产render函数做准备;

阅读全文 »

1. !DOCTYPE 标签:

它是指示 web 浏览器关于页面使用哪个 HTML 版本进行编写的指令.

2. H5新特性

媒体播放的 video 和 audio
本地存储 localStorage 和 sessionStorage
离线应用 manifest
桌面通知 Notifications
语意化标签 article、footer、header、nav、section
增强表单控件 calendar、date、time、email、url、search
地理位置 Geolocation
多任务 webworker
全双工通信协议 websocket
历史管理 history
跨域资源共享(CORS) Access-Control-Allow-Origin
页面可见性改变事件 visibilitychange
跨窗口通信 PostMessage
Form Data 对象
绘画 canvas

3. 行内元素及块级元素

  1. HTML4中,元素被分成两大类: inline (内联元素)与 block(块级元素)。一个行内元素只占据它对应标签的边框所包含的空间。
    常见的行内元素有: a b span img strong sub sup button input label select textarea

  2. 块级元素占据其父元素(容器)的整个宽度,因此创建了一个“块”。
    常见的块级元素有: div ul ol li dl dt dd h1 h2 h3 h4 h5 h6 p

  3. 区别
    (1)格式上,默认情况下,行内元素不会以新行开始,而块级元素会新起一行。
    (2)内容上,默认情况下,行内元素只能包含文本和其他行内元素。而块级元素可以包含行内元素和其他块级元素。
    (3)行内元素与块级元素属性的不同,主要是盒模型属性上:行内元素设置 width 无效,height 无效(可以设置 line-height),设置 margin 和 padding 的上下不会对其他元素产生影响。

4. 自闭和标签

br hr img input link meta

阅读全文 »