一、 var、let和const的区别?
①var声明的变量会挂载在window上,而let和const声明的变量不会
var a = 100;
console.log(a,window.a); // 100 100
let b = 10;
console.log(b,window.b); // 10 undefined
const c = 1;
console.log(c,window.c); // 1 undefined
②var声明变量存在变量提升,let和const不存在变量提升
console.log(a); // undefined ===> a已声明还没赋值,默认得到undefined值
var a = 100;
console.log(b); // 报错:b is not defined ===> 找不到b这个变量
let b = 10;
console.log(c); // 报错:c is not defined ===> 找不到c这个变量
const c = 10;
③let和const声明形成块作用域,而var声明的变量作用域为函数作用域或全局作用域
if(1){
var a = 100;
let b = 10;
const c = 1;
}
console.log(a); // 100
console.log(b) // 报错:b is not defined ===> 找不到b这个变量
console.log(c) // 报错:c is not defined ===> 找不到c这个变量
④同一作用域下let和const不能声明同名变量,而var可以
var a = 100;
console.log(a); // 100
var a = 10;
console.log(a); // 10
let a = 100;
let a = 10;
// 控制台报错:Identifier 'a' has already been declared ===> 标识符a已经被声明了。
⑤const一旦声明必须赋值,不能使用null占位;声明后不能再修改;如果声明的是复合类型数据,可以修改其属性
const a;//报错
const a = 100; //一旦声明必须赋值,不能使用null占位
const a = 100;
a = 10;//报错,声明后不能再修改
const obj = {a:100};
obj.name = 'apple';
obj.a = 10000;
console.log(obj); // {a:10000,name:'apple'},如果声明的是复合类型数据,可以修改其属性
⑥暂存死区
var a = 100;
if(1){
a = 10;
//在当前块作用域中存在a使用let/const声明的情况下,给a赋值10时,只会在当前作用域找变量a,
// 而这时,还未到声明时候,所以控制台Error:a is not defined
let a = 1;
}
二、ES5与ES6数组去重?
1.ES5数组去重:使用indexOf(可返回某个指定的字符串值在字符串中首次出现的位置,如果首次出现,返回-1)
var arr = [1,2,3,1,3,4,5];
Array.prototype.myInfo = function(){
var newArr = [];
for(var i=0;i<arr.length;i++){
var a = newArr.indexOf(arr[i]);
if(a == -1){
newArr[newArr.length] = arr[i];
}
}
return newArr;
}
var result = arr.myInfo();
console.log(result);
2.ES6数组去重
(1)Set和Array.from
①ES6中新增了Set数据结构,类似于数组,但是它的成员都是唯一的 ,其构造函数可以接受一个数组作为参数
let array = [1, 1, 1, 1, 2, 3, 4, 4, 5, 3];
let set = new Set(array);
console.log(set);
// => Set(5) [1, 2, 3, 4, 5]
②ES6中Array新增了一个静态方法Array.from,可以把类似数组的对象转换为数组
let set = new Set();
set.add(1).add(2).add(3);
let array = Array.from(set);
console.log(array);
// => Array(3) [1, 2, 3]
③于是,现在我们可以用一行代码实现数组去重了
let array = Array.from(new Set([1, 1, 1, 2, 3, 2, 4]));
console.log(array);
// => Array(4) [1, 2, 3, 4]
(2)Set和rest
rest 方法操作符为“…”;… 将字符从数组中剥离出来
let arr = [1,2,1,2,3];
let result = new Set(arr);
console.log(result); // => Set(3) [1, 2, 3]
console.log(...result); // => 1, 2, 3
console.log([...result]); // => Array(3) [1, 2, 3]
console.log([...new Set(arr)]); // => Array(3) [1, 2, 3]
三、vue的生命周期?父子组件生命周期?
Vue实例需要经过创建、初始化数据、编译模板、挂载DOM、渲染、更新、渲染、卸载等一系列过程,这个过程就是Vue的生命周期,Vue中提供的钩子函数有beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestroy、destroyed。
父子组件嵌套时,父组件和子组件各拥有各自独立的钩子函数。
1.创建过程
创建过程主要涉及beforeCreate、created、beforeMount、mounted四个钩子函数。
父beforeCreate ->父Created -> 父BeforeMount ->子BeforeCreate ->子Created ->子BeforeMount ->子Mounted ->父Mounted
2.更新过程
更新过程主要涉及beforeUpdate、updated两个钩子函数,当父子组件有数据传递时才会有生命周期的比较。
父BeforeUpdate ->子BeforeUpdate ->子Updated ->父Updated
3.销毁过程
销毁过程主要涉及beforeDestroy、destroyed两个钩子函数,本例直接调用vm.$destroy()销毁整个实例以达到销毁父子组件的目的。
父BeforeDestroy ->子BeforeDestroy ->子Destroyed ->父Destroyed
四、微信小程序页面跳转接口?
(1)wx.navigateTo():保留当前页面,跳转到应用内的某个页面。但是不能跳到tabbar页面
onLoad: function(options) {
wx.navigateTo({
url: '../index/index'
})
}
(2)wx.redirectTo():关闭当前页面,跳转到应用内的某个页面。但是不能跳转到tabbar页面
(3)wx.navigateBack():关闭当前页面,返回上一页面或多级页面。可通过getCurrentPages()获取当前的页面栈,决定需要返回几层
onLoad: function(options) {
var pages = getCurrentPages()
var num = pages.length
navigateBack:function(){
wx.navigateBack({
delta: num
})
}
}
(4)wx.switchTab():跳转到tabBar页面,并关闭其他所有非tabBar页面
(5)wx.reLaunch():关闭所有页面,打开到应用内的某个页面
拓展1:wx.navigateTo和wx.redirectTo有什么区别?分别适用什么场景?
①使用wx.navigateTo每新开一个页面,页面栈大小加1,使用wx.navigateTo重复打开页面也会增加页面栈。
②使用wx.redirectTo会关闭当前页面打开新页面,页面栈大小不变
③对于可逆操作,使用wx.navigateTo,比如从首页跳转到二级页面,从二级页面返回是不需要重新渲染首页。
④对于不可逆操作,使用wx.redirectTo,比如用户登录成功后,关闭登录页面,不能返回到登录界面。
⑤不要在首页使用wx.redirectTo,这样会导致应用无法返回首页。
拓展2:页面携带数据跳转
①方法1:API跳转页面时携带参数,在目标页面通过options.获取数据复制给本地变量
test1页面
// pages/test1/test1.js
Page({
/**
* 页面的初始数据
*/
data: {
name:'Tom',
age:'12'
},
buttonListener:function(){
var that = this
wx.navigateTo({
url: '/pages/test2/test2?nameData=' + that.data.name + '&ageData=' + that.data.age
})
}
})
<!--pages/test1/test1.wxml-->
<view>
<text>姓名:{{name}}</text>
</view>
<view>
<text>年龄:{{age}}</text>
</view>
<button bindtap='buttonListener'>携带数据跳转</button>
test2页面
// pages/test2/test2.js
Page({
/**
* 页面的初始数据
*/
data: {
name:null,
age:null
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
var that = this
that.setData({
name:options.nameData,
age:options.ageData
})
}
})
<!--pages/test2/test2.wxml-->
<view>
<text>姓名为:{{name}}</text>
</view>
<view>
<text>年龄为:{{age}}</text>
</view>
test1页面:
- Button点击事件:案例中采用了点击button触发跳转事件,所以要为button添加一个监听函数。在test1.wxml页面的中添加bindTap属性,来命名该button的监听函数名称。然后在test1.js中实现该监听函数即可
- 跳转函数:微信小程序提供自带的页面跳转函数,具体可参考微信小程序API
test2页面:
- 接收数据:使用onLoad函数在加载页面时接收数据,通过options.数据a的名称来获取数据a的内容,并赋值给本地变量
②方法2:使用全局数据存储
将要传递的数据,存储在App对象上(比如globalData属性)。
将要传递的数据,存储在小程序的本地数据缓存(Storage)中。
例如,我们在将要退出页面B的时候,作如下调用:
//=== 1. 存储到app对象上的方式 ========
var app = getApp()
app.globalData.mydata = {a:1, b:2}; //存储数据到app对象上
wx.navigateBack(); //返回上一个页面
//=== 2.存储到数据缓存的方式 =========
wx.setStorage({
key: "mydata",
data: {a:1, b:2},
success: function () {
wx.navigateBack(); //返回上一个页面
}
})
这样一来,当返回到上一个页面的时候,可以通过读取这些全局存储区域,来获取到我们需要的数据。
不过,这种方式也是有很明显的缺点的。由于是全局数据存储,所以当你存入了那些数据后,必须谨慎的去管理这些全局数据(何时被销毁),否则一不小心,就会产生副作用。
③方法3:从页面路由栈中直接获取和操作目标Page对象
这种方式,是通过调用小程序的API:getCurrentPages(),来获取当前页面路由栈的信息,这个路由栈中按照页面的路由顺序存放着相应的Page对象,我们可以很容易的获取到上一级页面的完整Page对象,从而使直接调用Page对象的属性和方法成为可能。如下所示:
var pages = getCurrentPages();
var currPage = pages[pages.length - 1]; //当前页面
var prevPage = pages[pages.length - 2]; //上一个页面
//直接调用上一个页面的setData()方法,把数据存到上一个页面中去
prevPage.setData({
mydata: {a:1, b:2}
})
比起全局数据存储的方式,这种方式在逻辑上要清晰得多,也不存在对数据的销毁有额外的管理工作。
五、冒泡排序?
冒泡排序算法的原理如下:
- 比较相邻的元素,如果第一个比第二个大,就交换他们。
- 对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
- 针对所有的元素重复以上的步骤,除了最后一个。
- 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
js实现冒泡排序
// 封装成函数
function bubble(arr){
if(arr instanceof Array && arr.length > 1){
//外层循环,控制趟数,每一次找到一个最大值
for (var i = 0; i < arr.length - 1; i++) {
// 内层循环,控制比较的次数,并且判断两个数的大小
for (var j = 0; j < arr.length - 1 - i; j++) {
// 如果前面的数大,放到后面(从小到大的冒泡排序)
if (arr[j] > arr[j + 1]) {
var temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
return arr; // 将执行完的结果返回就可以
}
}
var arr = [3,5,1,2,9,8,4,5,3,4];
console.log(bubble(arr)); // [1, 2, 3, 3, 4, 4, 5, 5, 8, 9]
拓展1:十大排序算法的时间复杂度与空间复杂度
- 稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面;
- 不稳定:如果a原本在b的前面,而a=b,排序之后a可能会出现在b的后面;
- 内排序:所有排序操作都在内存中完成;
- 外排序:由于数据太大,因此把数据放在磁盘中,而排序通过磁盘和内存的数据传输才能进行;
- 时间复杂度:一个算法执行所耗费的时间。
- 空间复杂度:一个算法在运行过程中临时占用存储空间大小。
拓展2:快速排序
快速排序是对冒泡排序的一种改进,它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
function quickSort(arr){
if(arr.length<=1){
return arr;
}
var left = [],
right = [],
pivot = arr.splice(0, 1)[0];
for(var i = 0;i<arr.length;i++){
if(arr[i]>pivot){
right.push(arr[i]);
}else{
left.push(arr[i]);
}
}
return quickSort(left).concat([pivot],quick(right));
}
六、24点游戏算法?
描述:输入1-10的任意四个数(可以重复),通过加减乘除得到24。
解决方法:
穷举法:
①穷举四个数的位置组合 (a b c d) (a b d c) (...)
②穷举标点符号组合 (a+b+c+d) (a+b+c-d) (...)