javascript学习

1.JavaScript中的数据类型

//在js中,可以分两种类型:
基本类型和引用类型。【两者区别在于存储位置不同。】

基本类型有:Number, String, Boolean, Undefined, null, symbol。

引用类型:Object, array, function, Date, Map, Set, RegExp等。

object: 创建object常用方式为对象字面量表示法,属性名可以是字符串或数值。
function: 函数实际上是对象,每个函数都是function类型的实例,而function也有属性和方法,跟其他引用类型一样。
函数存在三种常见的表达方式:
1.函数声明
function sun (x,y){ return x+y; }
2.函数表达式
let sum = function(x,y){ return x+y; }
3.箭头函数
let sum = (x,y)=>{ return x+y; }

基本类型和引用类型不同点在于:
基本类型存储在栈中,引用类型的对象存储在堆中。

引用数据存放在堆中,每个堆内存对象都有对应的引用地址指向它,引用地址存放在栈中。

数据类型:
布尔值Boolean:true/false.
null:一个表明null值得特殊关键字。
undefined:表示变量未赋值时的属性。
数字Number: 整数或浮点数。
任意精度的整数BigInt:可以安全地存储和操作打证书,甚至可以超过数字的安全整数限制。
字符串String: 字符串是一串表示文本值的字符序列。
代表Symbol:一种实力是唯一且不可改变的数据类型。
对象Object

2.数组常用方法

1.数组基本操作可以归纳为增删改查,需要注意的是哪些方法会对原数组产生影响,哪些不会。
例如:
增:【YES代表会对原数组产生影响,NO代表不会对原数组产生影响】
push(); // YES, 添加到数组末尾
unshift(); // YES, 在数组开头添加
splice(); // YES, 可接收三个参数,分别是 开始位置, 0(要删除的元素数量), 插入的元素; 返回空数组。
concat(); // NO
删:
pop();// YES, 用于删除数组的最后一项
shift();// YES,用于删除数组的第一项
splice(); // YES,可接收2个参数,分别是开始位置,删除元素的数量; 返回包含删除元素的数组。
slice(); //NO,用于创建一个包含原有数组中一个或多个元素的新数组,不会影响原始数组。
eg: let colors = ['red', 'green','blue','yellow','purple']
let colors2 = colors.slice(1);// green, blue, yellow, purple
let colors3 = colors.splice(1,4); // green, blue, yellow

改:即修改原数组,常用splice
splice()
查:即查找元素,返回元素索引值或值。
indexOf(); // 如果没有找到则返回-1。如果存在则返回元素索引值。
includes(); // 存在则返回true,反之为false。
find(); //返回第一个匹配的元素。

2.排序方法: reverse, sort
reverse: 用于将数组元素进行反转。
sort: 接收一个比较函数,用于判断哪个值应该排在前面。
function comp(n1,n1){
if(n1<n2){
return -1;// 升序排列
}else if(n1>n2){
return 1; // 降序排列
}else{
return 0;
}
}
3.转换方法
join(): 传入字符串分隔符,返回包含所有项的字符串。
eg: console.log(arr.join('-'));

4.迭代方法【都不改变原数组】
some(); //只要有一个存在都返回true,全都不存在才返回false。
every(); //全部存在则返回true,反之返回false
forEach(); // 无返回值
filter(); //返回true的项会组成数组之后返回。
map(); //返回由每次函数调用的结果构成的数组

3.JavaScript字符串方法

增: 除了常用的 + 以及 ${}进行字符串拼接之外,也可用concat方法
concat();// 用于将一个或多个字符串拼接成一个新字符串。

删:
slice();
substr();
substring(); // 这三个方法都可接收一个或两个参数进行截取操作。
改:
trim();
trimLeft();
trimRight(); // 上面三个方法是删除前,后,前后所有空格符,再返回新的字符串。
repeat(); // 接收一个整数参数,表示要将字符串复制多少次,然后返回操作后结果。
padStart();
padEnd(); // 上面两个方法是 如果某个字符串不够指定长度,会在头部或尾部补全。
toLowerCase();
toUpperCase(); // 大小写转化

查:
chatAt();//返回给定索引位置的字符
indexOf() //返回索引位置,不存在返回-1。
startWith()
includes() // 上面两个方法,存在返回true,反之返回false。

转换方法:
split: 把字符串按照指定的分割符,拆分成数组中的每一项。

4.==和===区别

==: 等于操作符,在比较中会先进行类型转换,再确定数值是否相等。
遵循一下规则:
两个简单类型,字符串和布尔值都会转换成数值,再比较。
简单类型与引用类型比较,,对象转化成其原始类型的值,再比较。
两个都为引用类型,则比较他们是否指向同一个对象。
null与undefined相等。
存在NaN则返回false。

===: 全等操作符,只有两个操作数在不转换的前提下相等才返回true。即类型相同,值相同。

null === null;//true
null == undefined; // true
null === undefined; // false
undefined === undefined; //true

5.深浅拷贝

浅拷贝:创建新的数据,这个数据有着原始数据属性值的一份精确拷贝。
如果属性是基本类型,拷贝的就是基本类型的值。如果属性是引用类型,拷贝的就是内存地址。

function shallowClone(obj){
const newObj = {};
for(let prop in obj){
if(obj.hasOwnProperty(prop)){
newObj[prop] = obj[prop]
}
}
return newObj;
}
实现浅拷贝方法有:
Object.assign;
Array.prototype.slice(), Array.prototype.concat(),
使用拓展运算符实现复制。

深拷贝:开辟一个新的栈,两个对象属性完全相同,但对应两个不同的地址,修改一个对象的属性,不会改变另一个对象的属性。
常见深拷贝方法有:
_.cloneDeep();
jQuery.extend();
JSON.parse(JSON.stringify());
手写循环递归。
function deepClone(obj, hash = new WeakMap()){
if(obj === null) return obj;
if(obj instanceof Date) return new Date(obj);
if(obj instanceof RegExp) return new RegExp(obj);
if(typeof obj !== 'object') return obj;
if(hash.get(obj)) return hash.get(obj);
let cloneObj = new obj.constructor();
hash.set(obj, cloneObj);
for(let key in obj){
if(obj.hasOwnProperty(key)){
cloneObj[key] = deepClone(obj[key], hash);
}
}
return cloneObj;
}

注意:拷贝类型为引用类型时,浅拷贝是拷贝一层,属性为对象时,浅拷贝是复制,两个对象指向同一个地址;
深拷贝是递归拷贝深层次,属性为对象时,深拷贝是新开栈,两个对象指向不同的地址。

6.闭包

定义:可以在一个内层函数中访问到其外层函数的作用域。

使用场景:
创建私有变量和延长变量的生命周期。

7.作用域链

作用域:即变量(变量作用域又称上下文)和函数能被访问的区域。
作用域分类:
全局作用域:任何不再函数中或是大括号中声明的变量,都是在全局作用域下,
全局作用域下声明的变量可以在程序的任意位置访问。
函数作用域:如果一个变量是在函数内部声明的,它就在这个函数作用域下面。
块级作用域:es6引入了let 和const关键字,在大括号中使用let和const声明的变量存在于块级作用域中。
大括号之外不能访问这些变量。

作用域链:即在当前作用域下寻找该变量,如果没有找到,则一层一层的往上寻找,直到找到或已经到了全局作用域。

8.JavaScript原型,原型链

每个对象都有一个原型对象prototype。

原型对象也能拥有原型并从中继承方法和属性,一层一层,依此类推。这种关系被称为原型链。它解释了为何一个对象会拥有
定义在其他对象中的属性和方法。在对象实例和它的构造器建立一个链接(它是_proto_属性,是从构造函数的prototype
属性派生的),之后通过上溯原型链,在构造器中找到这些属性和方法。

例如:
function Person(name){
this.name = name;
this.age = 18;
this.sayHello = function(){
console.log('hello,'+this.name)
}
}
//创建实例
let person = new Person('world')

_proto_指向创建它的构造函数的原型对象。
每个对象的_proto_都是指向它的构造函数的原型对象 prototype。
即: person1._proto_ === Person.prototype

原型对象是一个普通对象,而普通对象的构造函数都是Object。
Person.prototype._proto_ === Object.prototype

函数对象都是Function构造器产生的: Object._proto_ === Function.prototype

Object的原型对象也有 _proto_ 属性指向null, null是原型链的顶端: Object.prototype._proto_ === null;

9.JavaScript如何实现继承?

常见继承方式:
原型链继承:
例如:
function Parent(){
this.name = 'xixi';
}
Parent.prototype.getName = function(){
console.log(this.name)
}
function Child(){
this.age = 18;
}
Child.prototype = new Parent();
Child.prototype.getAge = function(){
return this.age;
}
构造函数继承(借助call):
例如:
function Parent(){
this.name = 'xx';
}
Parent.prototype.getName = function(){ return this.name }
function Child(){
Parent.call(this);
this.type = 'child';
}
let child = new Child();
组合继承
寄生式继承
原型式继承:
借助Object.create方法实现普通对象的继承。
寄生组合式继承:


还有ES6提供的 extends关键字直接实现JavaScript继承:
class Person {
constructor(name){ this.name = name }
getName(){
console.log(this.name)
}
}
class Child extends Person {
constructor(name,age){
super(name)
this.age = age;
}
}
const child1 = new Child('xixi', 18);

10.this对象

this关键字是函数运行时自动生成的一个内部对象,只能在函数内部使用,总指向调用它的对象。

默认绑定:全局环境中定义person函数,内部使用this关键字。
隐式绑定:函数还可以作为某个对象的方法调用,这时this就指这个上级对象。
new绑定:通过构建函数new关键字生成一个实例对象,此时this指向这个实例对象。
显示修改:apply(),call(),bind()是函数的一个方法,作用是改变函数的调用对象。
它的第一个参数就表示改变后调用这个函数的对象。
箭头函数:this指向在编译时就已经确定。一层一层向外找,直到找到第一个this的定义。


call,apply,bind作用是改变函数执行时的上下文,即改变函数运行时的this指向。
三者区别在于:
apply:接收两个参数,第一个参数是this的指向,第二个参数是函数接收的参数,以数组的形式传入。
function fn(...args){ console.log(this.args }
let obj = {name:'xx'}
fn.apply(obj, [1,2]); // this会变成传入的obj,传入的参数必须是一个数组。
fn(1,2);//this指向window

call: 第一个参数也是this的指向,后面传入的是一个参数列表。
fn.call(obj,1,2);// this会变成传入的obj
fn(1,2);
bind: 第一个参数是this的指向,后面传入的是一个参数列表也可多次传入,改变this指向后不会立即执行;
而是返回一个永久改变this指向的函数。
const bindFn = fn.bind(obj); // this会变成传入的obj,bind不是立即执行需要执行一次。
bindFn(1,2);//this指向obj

注意:三个方法中如果没有第一个参数,或参数为undefined,null,则默认指向全局window。

11.JavaScript中执行上下文和执行栈是什么?

执行上下文类型分为3种:
全局执行上下文:只有一个,浏览器中的全局对象就是window对象,this执行这个全局对象。
函数执行上下文:只有在函数被调用的时候才会被创建,每次调用函数都会创建一个新的执行上下文。
eval函数执行上下文:运行在eval函数中的代码。

执行上下文的生命周期:创建阶段-执行阶段-回收阶段。
创建阶段:即当函数被调用,但未执行任何其内部代码之前。
执行阶段:在这阶段,执行变量赋值,代码执行。
回收阶段:执行上下文出栈等待虚拟机回收执行上下文。


执行栈:也叫调用栈,具有后进先出结构,用于存储在代码执行期间创建的所有执行上文。

12.JavaScript中的事件模型

javascript中的事件,即在HTML或浏览器中发生的一种交互操作,使得网页具备互动性,常见的有加载事件,鼠标事件,
自定义事件等。

事件流会经历三个阶段:事件捕获阶段-处于目标阶段-事件冒泡阶段。

事件模型分为三种:
原始事件模型:
事件绑定监听函数简单,直接绑定onclick="fun"/通过js代码绑定 btn.onclick=function(){}
标准事件模型:
addEventListener('click', fun, false)
function fun(event){}
removeEventListener(eventType, handler, useCapture);
会经历三个过程:事件捕获阶段-事件处理阶段-事件冒泡阶段
IE事件模型:
两个过程: 事件处理阶段-事件冒泡阶段。
通过 attachEvent(eventType, handler) 绑定监听函数
通过 detachEvent(eventType, handler) 移除监听函数

13. typeof 与 instanceof 区别?

typeof 返回一个字符串,表示这个值的数据类型。

instanceof: 用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。

两者区别:
typeof 返回一个变量的基本类型,instanceof返回的是一个布尔值。
instanceof可以准确的判断复杂引用数据类型,但不能正确判断基础数据类型。
typeof 可以判断基础数据类型(除null外),但是引用数据类型中,除了function类型以外,其他无法判断。

如果需要通用检测数据类型,可以采用 Object.prototype.toString, 调用该方法,统一返回格式"[object xxx]"的
字符串。

14.如何理解事件代理?

事件代理:就是把一个元素响应事件的函数委托到另一个元素。

例如: 点击input动态添加元素
<input type="button" name="" id="btn" value="添加" />
<ul id="ul">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
const btn = document.getElementById('btn');
const ul = document.getElementById('ul');
const num = 4;
// 事件委托,添加的子元素也有事件
ul.onclick = function(e){
e = e || window.event;
const target = e.target || e.srcElement;
if(target.nodeName.toLowerCase() == 'li'){
console.log(target.innnerHTML);
}
}
btn.onclick = function(){
num++;
const li = document.createElement('li');
li.innerHTML = `{num}`;
ul.appendChild(li);
}

总结:适合事件委托的有 click, mousedown, mouseup, keydown, keyup, keypress
优点: 减少整个页面所需的内存,提升整体性能。动态绑定,减少重复工作。

15.new操作符

在JavaScript中,new操作符用于创建一个给定构造函数的实例对象。
例如:
function Person(name,age){
this.name = name;
this.age = age;
}
Person.prototype.sayHello = function(){
console.log(`hello,${this.name}`);
}
const person = new Person('tony',18);
console.log(person) // Person{name: 'tony' ,age:18}
person.sayhello();// hello,tony

即:new关键字做了一下工作
创建一个新的对象obj,将对象与构建函数通过原型链连接起来,将构建函数中的this绑定到新建的对象obj上;
根据构建函数返回类型作判断,如果是原始值则被忽略,如果是返回对象,需要正常处理。

例如: function testnew(func, ...args){
const obj = {}; //创建新对象
obj._proto_ = func.prototype; //新对象原型指向构造函数原型对象
let result = func.apply(obj, args); //将构建函数的this指向新对象
return result instanceof Object ? result : obj; //根据返回值判断
}

16.ajax

ajax通过 XmlHttpRequest 对象向服务器发送异步请求,从服务器获取数据,然后用JavaScript来操作DOM从而更新页面。

实现过程:
通过XMLHttpRequest()构造函数用于初始化一个 XMLHttpRequest实例对象:
const xhr = new XMLHttpRequest();
通过XMLHttpRequest对象的open()方法与服务器建立连接:
xhr.open(method, url)
通过XMLHttpRequest 对象的 send()方法,将客户端页面的数据发送给服务端:
xhr.send()
onreadystatechange事件用于监听服务器端的通信装态,主要监听的属性为 XMLHttpRequest.readyState
xhr.onreadystatechange = function(){
if(xhr.readyState === 4) {
if(xhr.status === 200){
// 成功返回数据
}else{
// 请求错误
}
}
}

17.常见DOM操作

创建节点:
const div = document.createElement('div');//创建新元素
const text = document.createTextNode('content');//创建一个文本节点;
const dataAttr = document.createAttribute('custom');//创建属性节点,可以是自定义属性
查询节点:
querySelector 传入任何有效的css选择器,即可选中单个DOM元素(首个):
  document.querySelector('.element');
  document.querySelector('#element');
  document.querySelector('div')
  document.querySelector('[name="username"]')

querySelectorAll: 返回一个包含节点子树内所有与之相匹配的element节点列表,
如果没有相匹配的,则返回一个空节点列表。
更新节点:
innerHTML: 修改一个DOM节点的文本内容。
innerText,textContent: 自动对字符串进行HTML编码
style:DOM节点的style属性对应所有的css,可以直接获取或设置。

添加节点:
innerHTML:
appendChild:
insertBefore:
setAttribute: 指定元素中添加一个属性节点。
删除节点:
removeChild: 删除一个节点,首先先获取该节点本身以及它的父节点。

获取DOM元素方法:
document.getElementById('id属性值') //返回指定id对象的引用
document.getElementsByClassName('class属性值') //返回拥有指定class的对象集合
document.getElementsByTagName('标签名') //返回指定标签名的对象集合
document.getElementsByName('name属性值') //返回拥有指定名称的对象集合
document/element.querySelector('css选择器') //返回第一个匹配的元素
document.querySelectorAll('css选择器'); //返回所有匹配的元素
document.documentElement //获取页面中的HTML标签
document.body; //获取页面中的body标签
document.all[''];//获取页面中所有元素节点的对象集合

18.BOM

BOM: 浏览器对象模型,提供独立于内容与浏览器窗口进行交互的对象。


window:即是浏览器窗口的一个接口,又是全局对象。
window.open(), window.close();
scrollTo(x,y), scrollBy(x,y);
resizeTo(w,h);

location:
location.reload(): 重新刷新当前页面

navigator:用来获取浏览器的属性。

screen:显示浏览器窗口外面的客户端显示器信息。

history: 用来操作浏览器URL的历史记录。
history.go();
history.forward();
history.back();
history.length;

19.javascript本地存储方式

四种存储方式:
cookie: 可用于辨别用户身份存储在用户本地终端上的数据。
sessionStorage:
跟localStorage使用方法基本一致,唯一不同的是生命周期,一旦页面关闭,sessionStorage将会删除数据。
localStorage:
生命周期:持久化的本地存储,除非主动删除数据,否则数据是永远不会过期。
存储的信息在同一域中是共享的。
大小:5M
localStorage本质上是对字符串的读取,如果存储内容多的话会消耗内存空间,会导致页面变卡。
受同源策略的限制。
indexedDB:用于客户端存储大量结构化树。

cookie,sessionStorage,localStorage三者区别:
存储大小:cookie数据大小不能超过4k,sessionStorage/localStorage可以达到5M或更大。
有效时间:localStorage存储持久数据,浏览器关闭后数据不丢失除非主动删除数据;
sessionStorage数据在当前浏览器窗口关闭后自动删除;
cookie设置的cookie过期时间之前一致有效,即使窗口或浏览器关闭。
数据与服务器之间的交互方式:cookie的数据会自动的传递到服务器,服务器端也可以写cookie到客户端;
sessionStorage/localStorage不会自动把数据发给服务器,仅在本地保存。

20.事件循环

事件循环是: 同一时间内只能做一件事,但这并不意味着单线程就是阻塞,而实现单线程非阻塞的方法就是事件循环。

在JavaScript中,所有的任务分为:
同步任务:立即执行的任务,同步任务一般会直接进入到主线程中执行。
异步任务:异步执行的任务,比如Ajax网络请求,setTimeout定时函数等。

异步任务又分为:微任务和宏仁务。
一个需要异步执行的函数,执行时机是在主函数执行结束之后,当前宏仁务结束之前。
常见的微任务有: Promise.then, MutationObserver, process.nextTick
宏仁务有: script, setTimeout/setInterval, postMessage, MessageChannel, setImmediate, I/O

事件循环执行机制:
执行一个宏仁务,如果遇到微任务就将它放到微任务的事件队列中,当前宏仁务执行完成后,会查看微任务的事件队列,
然后将里面的所有微任务一次执行完。
例如:
console.log(1)
setTimeout(()=>{ console.log(2) },0)
new Promise((resolve, reject)=>{
console.log('new promsie')
resolve()
}).then(()=>{
console.log('then')
})
console.log(3)
// 上面打印顺序为: 1 - new promise - 3 - then - 2

async/await: async用来声明一个异步方法, await用来等待异步方法执行。

async函数返回一个promise对象。
await后面是一个promise对象,返回该对象的结果。如果不是promise对象,就直接返回对应的值。
不管await后面跟着什么,await都会阻塞后面的代码,后面的代码会进入微任务队列中。
例如:
async function f1(){
console.log(1);
await f2();
console.log(2);
}
async function f2(){
console.log('f2')
}
f1();
console.log(3);
打印顺序为: 1 - f2 - 3 - 2

21.javascript如何实现函数缓存

函数缓存:将函数运算过的结果进行缓存。

实现函数缓存主要依靠闭包,柯里化,高阶函数。

闭包可以理解成:函数+函数体内可访问的变量总和。
例如:
(function(){
var a = 1;
function add(){
const b = 2;
let sum = b+a;
console.log(sum);
}
add();
})()

高阶函数:通过接收其他函数作为参数或返回其他函数的函数。
例如:
function foo(){
var a = 2;
function bar(){ console.log(a) }
return bar;
}
var baz = foo();
baz();//2

22.防抖节流

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

为了优化体验,需要对事件进行调用次数的限制,就可以采用防抖和节流的方式来减少调用频率。

节流:
function throttled(fn, delay){
let timer = null;
let starttime = Date.now();
return function(){
let curtime = Date.now(); //当前时间
let resttime = delay - (curtime - starttime);
let context = this;
let args = arguments;
clearTimeout(timer);
if(resttime <= 0){
fn.apply(context, args);
starttime = Date.now()
}else{
timer = setTimeout(fn, resttime);
}
}
}

防抖:
function debounce(fun, wait){
let timeout;
return function(){
let context = this;
let args = arguments;
clearTimeout(timeout);
timeout = setTimeout(function(){
fun.apply(context, args);
}, wait)
}
}
两者不同点:
函数防抖,在一段连续操作结束后,处理回调,利用 clearTimeout 和 setTimeout 实现。函数节流在一段
连续操作中,每一段时间只执行一次,频率较高的事件使用可以提高性能。
函数防抖关注一定时间连续触发的事件,只在最后执行一次,而函数节流一段时间内只执行一次。

应用场景:
防抖在连续的事件,只需触发一次回调的场景有:
搜索框搜索,在用户最后一次输入完,再发送请求。
手机号,邮箱验证输入检测。
窗口大小resize。只需窗口调整完成后,计算窗口大小,防止重复渲染。
节流在间隔一段时间执行一次回调的场景有:
滚动加载,加载更多或滚动底部监听。
搜索框。

23.javascript数字精度丢失

1. Number.EPSILON 方法可以解决 0.1+0.2 !==0.3 问题。
例如:
console.log(0.1+0.2 === 0.3);// false
function fn(){
if(0.1+0.2 -0.3 < Number.EPSILON){
return true;
}else{
return false;
}
}
console.log(fn()); // true
2. 可以把0.1,0.2,0.3扩大倍数进行比较,比如说都扩大10倍,100倍等等。

3.使用第三方库,Math.js, bigDecimal.js等

 24.大文件上传

分片上传: 就是将所要上传的文件,按照一定的大小,将整个文件分隔成多个数据块来进行分片上传。
流程:
1.将需要上传的文件按照一定的分隔规则,分隔成相同大小的数据块。
2.初始化一个分片上传任务,返回本次分片上传唯一标识。
3.按照一定的策略(串行或并行)发送各个分片数据块。
4.发送完成后,服务端根据判断数据上传是否完成,如果完整,则进行数据块合成得到原始文件。

25.原生如何实现上拉加载,下拉刷新

上拉加载:即页面触底
判断页面触底的几个属性:
scrollTop: 滚动视窗高度距离window顶部距离
clientHeight: 固定值,即屏幕可视区域的高度
scrollHeight: 即body所有元素的总长度
触底条件: scrollTop + clientHeight >= scrollHeight
let clientHeight = document.documentElement.clientHeight; //浏览器高度
let scrollHeight = document.body.scrollHeight;
let scrollTop = document.documentElement.scrollTop;
let distance = 50;//距离视窗还有50px的时候,开始触发
if((scrollTop+clientHeight) >= (scrollHeight - distance)){
console.log('开始加载数据');
}

下拉刷新:
流程为:
1.监听原生touchstart事件,记录初始位置,e.touches[0].pageY
2.监听原生touchmove事件,记录并计算当前滑动的位置值与初始位置值的差值,大于0表示向下拉动,
并借助CSS3的translateY属性使元素跟随手势向下滑动对应的差值,同时也应设置一个允许滑动的最大值。
3.监听原生的touchend事件,若此时元素滑动达到最大值,则触发callback,同时将translateY重置为0,元
素回到初始位置。
例如:
<main>
<p class="refresh-text"></p>
<ul id="refresh-container'>
<li>111</li>
<li>222</li>
...
</ul>
</main>
//监听touchstart事件,记录初始值
var refreshContainer = document.getElementById('refresh-container');
var refreshText = document.querySelector('.refresh-text')
var startPostion = 0; //初始值
var moveHeight = 0; //移动距离

refreshContainer.addEventListener('touchstart',function(e){
startPostion = e.touches[0].pageY;//记录初始位置
refreshContainer.style.position = 'relative';
refreshContainer.style.transition = 'transform 0s';
}, false)

 

学习链接:https://vue3js.cn/interview/

posted on 2022-10-13 12:38  有匪  阅读(17)  评论(0编辑  收藏  举报

导航