问题汇总:持续更新…
**
1.阻止冒泡的办法和阻止默认事件的办法?
**
阻止冒泡:
IE:e.cancelBubble = true;
chrome:e.stopPropagation();
阻止默认事件
e.preventDefault();
return false;//(暴力法)既阻止冒泡,又阻止默认事件
**
2.事件委托**
利用事件冒泡的原理,将触发函数绑定到父元素上,事件会从目标元素向逐级向上传递,由父元素获取并执行对应的触发事件。
**
3.对闭包的理解
**
什么是闭包?
MDN的定义:即能够访问自由变量的函数,就是闭包。何为自由变量,即不是函数的参数,又不是函数的局部变量。其实所有的函数,都是闭包。
实践角度:
即使创建它的上下文已经销毁,它仍然存在(比如,内部函数从父函数中返回),在代码中引用了自由变量。
具体实现:
function foo(){
var a = 10;
function bar(){
console.log(a);
}
return bar;
}
var b = foo();
b();//打印10
闭包的用处:
1.实现变量私有化
2.可做缓存
3.匿名自调用函数,实现块级化传参
闭包的缺点:
1.导致外部变量仍然拿着内部执行期上下文的引用。这将导致内部函数执行完后,其执行期上下文无法被彻底释放。
2.过多的使用闭包,会导致内存泄漏,应当在函数执行结束后,将不需要的闭包删除。
**
4.Eval
**
eval函数接收一个参数,如果是非字符串,直接返回。如果是字符串,翻译并执行该字符串,一般将可执行代码作为字符串传入,如果翻译后不合法报错,如果非法调用也会返回一个异常EvalError 。
问题:eval函数迫使js执行一段代码,有可能会改变this的指向。
**
5.如何理解跨域
略,可转我的一篇文章查看
6.Ajax提交与表单的区别
ajax是通过js进行部分数据提交,并且能够实现在页面的不刷新的情况下局部更新。注重用户体验。
表单提交是全部更改,表单提交相当于放弃本次的页面,重新从服务器下载新的页面,所以这将导致页面重新加载,用户体验较差。
7. local Storage、session Storage、cookie区别
**
local Storage本地存储,将数据存储在本地硬盘上,生命周期是永久的,即使关闭页面/浏览器,依然存在。除非主动删除。用于长期性登录
session Storage是会话存储,在同源的浏览器窗口,窗口不关闭,数据始终存在,刷新不影响。但是只要窗口关闭,数据就会被删除。用于敏感信息登录。
local Storage和session Storage的大小基本都在5M,并且都是保存在客户端,不与服务器交互,并且存储都是字符串格式,通过window对象获取。(window.localStorage/sessionStorage);
cookie还有session都是用来跟踪浏览器的一种会话方式。
cookie在客户端、session在服务端。
单个cookie大小为4kb,每次向服务端请求时都会携带,若不在浏览器设置cookie过期时间,被保存在硬盘中,直到浏览器关闭而结束。如果设置了过期时间,会一直保存在硬盘中,直到过期时间结束才消失。
session在服务端被创建,但是创建时需要sessionID,而sessionID是在cookie中加密保存的。如果请求时携带cookie,服务器会查找对应的session,若不携带cookie,请求时,服务端会创建新的session。可通过url重写将sessionID保存在url中。但是这种方式并不安全。
8. ajax原理,ajax核心对象,如何区分ajax同步异步,ajax的特点
Asynchronous JavaScript XML,即异步的javascript和xml,
核心对象XMLHttpRequest实现异步,早期使用xml进行数据传输现在前端js获取数据基本使用JSON、要使用javascript实现局部更新。一个实现局部无刷新更新的混合技术。
主要利用XMLHttpRequest异步对象,相当于在浏览器与服务器之间的一个中间层,通过该异步对象进行数据的请求和提交。
使用XMLhttpRequest对象来实现这一功能,也需要javascript来操作DOM实现局部的信息更新。
1.同步:就是用户填写完信息之后,全部提交给服务器,等待服务器的回应,是一次性全部的。
2.异步:当用户填写完一条信息之后,这条信息会自动向服务器提交,然后服务器响应客户端,在此过程中,用户依然在填写表格的信息,即向服务器请求多次,节省了用户的时间,提高了用户的体验。
XMLHttpRequest对象的出现分割了同步和异步。XMLHttpRequest出现之前是同步的,出现之后是异步的。
同步:页面请求实时传给服务器,导致必填数据没有填的时候,要回到页面上重新从头填写,耗时长、客户体验差。
异步:在页面必填项写上必填选项,不用通过传给服务器判断必填内容是否已经填写完整,耗时短、用户体验强。
区分ajax的异步同步:
原声方法的第三个参数为async属性,为true就是异步
jq中的async属性,为true就是异步(默认为true)。
特点:
实现无页面刷新获取服务器数据的混合技术。
具体实现过程:
1.创建异步对象
2.绑定监听函数,
3.打开连接
4.发送请求
再说说post和get的区别
get主要是请求数据
post主要是提交数据
从直接直接效果看,get是明文传输,post不是。所以get相对来说不安全。
但是post就真的安全吗?浏览器一审查,也可以看到。
所谓的url长度限制,http协议没有明确规定,只是因为各大浏览器给的限制或者是服务端的限制,get的请求参数放在header中,所以长度是有限的,而post的请求参数是放在body中,长度是无限的。
提交时,get产生1~2个tcp包,post至少产生2个,但有一个例外火狐。
其实最终到底,get和post都是基于tcp/ip的通信,而且get也可以加body,post也可以加querystring,http为了方便管理对他们进行了定义。再加上浏览器和服务器的限制,也就在数据长度上产生了差异。
9.node.js中exports和module.exports的区别。
两种暴露模块的方法
第一种
//a.js
exports.log =function (str) {
console.log(str);
}
//b.js
var s = require("./a");
s.log("hello");
最开始exports和module.exports都指向同一个对象{}。
这种种方式,是在给这个空对象{}添加属性,又因为module.exports也是指向这个对象的,
所以最终require方法返回的module.exports是指向了这个具有log方法的对象的,可以引用到模块。
第二种
//a.js
module.exports = function (str) {
console.log(str);
}
//b.js
var s = require("./a");
s("hello");
这种方式是让module.exports指向一片新的内存空间,exports指向的仍然是{},
但是由于require方法返回的是module.exports,所以最终也能引入模块。
第三种
//a.js
exports = function (str) {
console.log(str);
}
//b.js
var s = require("./a");
s("hello");
这种写法是让exports指向一片新的内存空间,module.exports指向的仍然还是{},
那么最终require方法是将module.exports返回,所以会导致报错,“s is not a function ”
10.一个页面中,父子页面间传值问题。
附两张表格
原生JS方法
jQuery方法
11.js页面触发事件
3个阶段:捕获阶段、目标阶段、冒泡阶段。
事件冒泡:作用于子元素上的事件会一级一级向上传递,类似于冒泡的形式。
事件捕获:作用于父元素的事件会一级一级向下传递到最终的子元素。
<div id="s1">
<div id="s2"></div>
</div>
在addEventListener中第三个参数 false 和 true 分别对应 事件冒泡 和 事件捕获
假设上面的代码, s1和s2都用addEventListener绑定了点击事件
当false时, 点击s2, 则先执行s2, 再执行s1, 即从点击的元素开始往父级冒泡
当true是, 点击s2, 则先执行s1, 再执行s2, 即按document -> html -> body -> s1 -> s2的顺序捕获。
12.使用jq实现元素的增删改查
- 查找
$(“过滤选择器”)
节点间关系查找 - 创建新元素:
var newele = $(“html代码”) - 增添:
$(“parent”).append(newele);
简化:$(“parent”).append(“html代码”);
在某个子元素之前插入或之后插入:
$(“child”).before/after(newele);
简化:$(“child”).before/after(“html代码”); - 删除:
$("…").remove(); - 替换
$(“现有元素”).replaceWith(“新元素html”);
$(“新元素html”).repalceAll(“现有元素”); - 克隆
$(’…’).clone();//仅复制属性、不复制行为
$(’…’).clone(true);//既复制属性,又复制行为
13.webpack打包
webpack是一个静态模块资源打包器,webpack在处理应用程序时,会递归的构建一个依赖关系图,其中包含所有的模块,最终将这些模块打包成一个资源包文件(bundle);
好处:打包完成后,客户端请求可以一次性加载,减少请求次数,从而提高访问效率。
如何使用:
1.创建客户端项目:
创建HTML/JS/CSS等文件
2.创建项目的描述文件:
npm init
3.下载webpack及其依赖模块,并添加为“开发阶段的依赖”
npm i webpack --save--dev
4.创建webpack项目的主配置文件webpack.config.js
module.exports={
mode:"production",
entry:'./src/index.js', //入口文件
output:{ //输出文件
path:__dirname+'/dist',
name:'bundle.js'
},
module:{ //运行模式
rules:[ //指定打包规则
{
test:/\.css$/,
use:['style-loader','css-loader']
},
{
test:/\.(jpg|png|gif)$/,
use:['url-loader']
}
]
},
plugins:[]
}
5.运行webpack,根据配置文件进行打包
node ./node_modules/webpack/bin/webpack.js
/*或者在package.json中配置scripts: "build": "webpack",
用 npm run build方式运行*/
14深度克隆
function deepClone(origin,target){
var target = target || {};
for(var prop in origin){
if(origin.hasOwnProperty(prop) ){
if(typeof (origin[prop]) == "Object" && origin[prop] !== null){
if(Object.prototype.tostring.call(origin[prop])=="object Array"){
target[prop] = [];
}else{
target[prop] = {};
}
deepClone(origin[prop],target[prop]);
}else{
target[prop] = origin[prop]
}
}
}
}
//hasOwnProperty在执行对象查找时,永远不会去查找原型。
15、继承方式(圣杯模式)
继承方式有很多种:原型链继承、借用构造函数、组合式继承、原型继承、圣杯模式等,最后一种是对最优的,常用的是原型+构造函数的组合式继承。
//原型链继承
function Parent(){
this.name = "deng";
}
Parent.prototype.sayName = function(){
console.log(this.name);
}
function Child(){}
Child.prototype = new Parent();//改变原型指向
var child = new Child();
console.log(child.name);
console.log(child.sayName());
/**
* 缺点:
* 1.引用类型属性被所有实例共享
* 2.在创建子类对象实例时,不能向父类传参
* 优点:
* 1.相同的方法可以从原型上继承而来,避免了重复生成代码
*/
//eg:
function Parent(){
this.prefer = ['music','sport'];
}
function Child(){}
Child.prototype = new Parent();
var child1 = new Child();
child1.prefer.push('reading');//["music", "sport", "reading"]
console.log(child1.prefer);
var child2 = new Child();
console.log(child2.prefer);//["music", "sport", "reading"];//都变了
//借用构造函数
function Parent(){
this.prefer = ['music','sport'];
}
function Child(){
Parent.call(this);
}
var child1 = new Child();
child1.prefer.push('reading');
console.log(child1.prefer);
var child2 = new Child();
console.log(child2.prefer);
/**
* 优点:
* 1.避免了引用类型属性被所有实例共享
* 2.可以在child中向Parent传参
*/
//eg:
function Parent(name){
this.name = name;
}
function Child(name){
Parent.call(this,name);
}
var child1 = new Child('deva');
console.log(child1.name);
var child2 = new Child('alice');
console.log(child2.name);
/**缺点:
* 1.方法都在构造函数中定义,每次创建实例都会创建一遍方法
*/
//组合式继承
function Parent(name){
this.name = name;
this.prefer = ['music','sport','reading'];
}
//公共方法
Parent.prototype.sayName = function(){
console.log('Hello I am '+this.name);
}
function Child(name,age){
Parent.call(this,name);
this.age = age;
}
Child.prototype = new Parent();//第一次new
var child1 = new Child('deva',18);//第二次new
child1.prefer.push('game');
console.log(child1.prefer);
console.log(child1.age);
console.log(child1.sayName());
var child2 = new Child('alice',20);
console.log(child2.name);
console.log(child2.age);
console.log(child2.prefer);
console.log(child2.sayName());
/**优点
* 融合原型链继承和构造函数继承的优点,最常用。
* 缺点:
* 1.使用了两次new;
* 2.Child.prototype上也被添加了属性
*/
//原型式继承
var person = {
name:'grandfa',
prefer:['code','rading']
}
function createObj(o){
function F(){};
F.prototype = o;
return new F();
}
var person1 = createObj(person);
var person2 = createObj(person);
person1.name = 'deva';
console.log(person1.name);//deva
console.log(person2.name);//grandfa
person2.name = "alice";
console.log(person2.name);//alcie
person1.prefer.push('game');
console.log(person1.prefer);//["code", "rading", "game"]
console.log(person2.prefer);//["code", "rading", "game"]
/**缺点
* 此法与原型链式继承基本相同,
*/
//寄生式集成
function createObj(o){
var obj = Object.create(o);
obj.sayName = function(){
console.log('hello');
}
return obj;
}
*/
/**缺点
* 跟构造函数模式一样,每次创建对象都会创建一遍方法
*/
//圣杯模式(寄生组合式继承)
(function (){
var F = function(){}
return inherit = function(target,origin){
F.prototype = origin.prototype;
target.prototype = new F();
target.prototype.constructor = target;
target.prototype.uber = origin.prototype;
}
})()
function Person(){}
Person.prototype.lastName = "Ming";
function Son(){}
inherit(Son,Person);
var son = new Son();
var person = new Person();
console.log(son.lastName);
16.创建变量的方式
创建变量的方式很多:
字面量、工厂模式、构造函数模式、原型模式、构造函数+原型的子和模式、寄生构造模式。
//字面量
var person = {
name:'tom',
age:18,
sayName:function(){
console.log(this.name);
}
}
/**缺点:
* 不能批量生产、以及对象的属性方法,若要修改都要手动添加
*/
//工厂模式
function createPerson(name){
var o = new Object();
o.name = name;
o.sayName = function(){
console.log(this.name);
};
return o;
}
var person1 = createPerson('deva');
console.log(person1 instanceof createPerson); //false
console.log(person1 instanceof Object); //true
/**缺点:
* 生成并返回了一个中间对象,不能判断对象的类型
*/
//构造函数模式优化
function Person(name){
this.name = name;
}
function sayName(){//相当于一个静态方法
console.log(this.name);
}
var person2 = new Person('alice');
console.log(person2 instanceof Person); //true
console.log(person2 instanceof Object); //true
/**优点:
* 可以判断每个实例的类型
* 缺点:
* 对于原始的构造函数每次创建实例,相同的方法都要被创建一次,
* 优化后的构造函数,虽解决了上述问题,但是破坏了封装。
*/
//原型模式
function Person(name){
}
Person.prototype = {
constructor:Person,
name:'deva',
sayName:function(){
console.log(this.name);
}
}
var person3 = new Person();
console.log(person3 instanceof Person); //true
console.log(person3 instanceof Object); //true
/**优点:
* 相同的方法不会重复创建
* 缺点:
* 不能初始化参数
*/
//构造函数与原型 的 组合模式
function Person(name){
this.name = name;
}
Person.prototype = {
constructor:Person,
sayName:function(){
console.log(this.name);
}
}
var person4 = new Person('deva');
var person5 = new Person('alice');
console.log(person5 instanceof Person); //true
console.log(person5 instanceof Object); //true
/**优点:
* 该共享的共享,该私有的私有,广泛使用
*/
//寄生构造函数模式
function Person(){
var o = new Object();
o.name = name;
o.sayName = function(){
console.log(this.name);
};
return o;
}
var person5 = new Person('deva');//就在这里比工厂模式多了一步new操作
console.log(person5 instanceof Person); //false
console.log(person5 instanceof Object); //true
/**缺点:
* 和工厂模式一样无法判断类型
*
*/
17.Doctype是什么?有什么作用?
Docype声明文档的类型,并且告知标记语言解析器应该以什么样的文档类型定义来解析此文档。
18.使用html查找与选择器查找的区别?
使用html查找,查询速度快,但是所查询到的结果是存储在一个动态集合中,这意味着不会被保存,每次使用都会重新查找。
使用选择器查找,所查询到的结果会被保存在一个非动态集合中,只要页面元素不发生改变,下次查找就会直接到集合中获取,不会重复查找。速度上在第一次查找时较之于html查找稍慢。
19.封装ajax
原生js封装
function ajax({url, method, body, successFn, failFn, headers}){
//1.创建异步对象
var xhr= new XMLHttpRequest()
//2.绑定监听事件
xhr.onreadystatechange = functin(){
if(xhr.readyState ==4){
if(xhr.status >= 200 && xhr.status <= 300){
successFn.call(undefined,xhr.responseText)
}else if(xhr.status >= 400){
failFn.call(undefined, xhr)
}
}
}
//打开连接
xhr.open(method, url)
for(let key in headers){
var value = headers[key]
xhr.setRequestHeader(key, value)
}
//发送请求
request.send(body)
}
Promise封装
function ajax({url,type,data,dataType}){
return new Promise(function(open,err){
//1. 创建xhr对象
var xhr=new XMLHttpRequest();
//2.绑定监听事件
xhr.onreadystatechange=function(){
if(xhr.readyState==4&&xhr.status==200){
if(dataType!==undefined
&&dataType.toLowerCase()==="json")
var res=JSON.parse(xhr.responseText);//转对象
else
var res=xhr.responseText;
open(res);
}
}
if(type.toLowerCase()=="get"&&data!=undefined){
url+="?"+data;
}
//3.打开连接
xhr.open(type,url,true);
if(type.toLowerCase()==="post")
//增加:设置请求消息头
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
//4.发送请求
if(type.toLowerCase()=="post"&&data!==undefined)
xhr.send(data);
else
xhr.send(null);
})
}
20.写一个function清除字符串前后的空格,兼容浏览器。
function trim(str){
return str.repalce(/(^\s+)/g,"").replace(/(\s+$)/g,"");
//也可以一次性// return repalce(/(^\s+)|(\s+$)/g);
}
写一个邮箱的正则:
特征1:zhangsan-01@163.com 或者 zhangsan-01@mail.com
/[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]{2,5}+)+$/
特征2:如果存在汉字:张三01Abc@lenovo.com.cn
汉字的匹配区间在:\u4e00-\u9fa5
^[A-Za-z0-9\u4e00-\u9fa5]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$
写一个手机的正则:
/(\+86|0086)?\s*1[3-8]\d{9}/
21.如何鉴别当前浏览器的版本信息
Window.prototype.getNavigator=getNavigator;
function getNavigator(userAgent){
var ua = userAgent || navigator.userAgent;
var browser,version;
if(ua.indexOf("MSIE")!== -1){
browser = "IE"
}else if(ua.indexOf("Trident")!== -1){
browser = "IE";
version = 11;
}else if(ua.indexOf("FireFox")!== -1){
browser = "firefox";
}else if(ua.indexOf("OPR")!== -1){
browser = "OPR";
}
else if(ua.indexOf("Chrome")!== -1){
browser = "Chrome";
}
else if(ua.indexOf("Safari")!== -1){
browser = "Safari";
}
if(version === undefined){
var i = ua.indexOf(browser)+browser.length + 1;
version = parseFloat(ua.slice(i,i+3));
}
return {browser,version}
}
22.数组去重(老生常谈)
var arr = [1,'1',2,2,'2',3,3,3];
//第一种:原始方法
function unique(arr){
var newArr = [];
for(var i = 0,len = arr.length; i < len; i ++){
for(var j = 0,newlen = newArr.length; j < newlen; j ++){
if(arr[i] === newArr[j]) break;
}
if(newlen === j){
newArr.push(arr[i]);
}
}
}
//第二种:使用indexOf
function unique(arr){
var newArr = [];
for(var i = 0,len = arr,length; i < len; i ++){
if(newArr.indexOf(arr[i]) < 0){
newArr.push(arr[i]);
}
}
return newArr;
}
//第三种使用ES5的filter
function unique(arr){
var newArr = arr.filter((item,index,arr)=>{
return arr.indexOf(item) === index;
})
return newArr;
}
//第四种ES5的forEach
function unique(arr){
var newArr = [];
arr.forEach((item,index,arr)=>{
if(newArr.indexOf(item) < 0){
newArr.push(item);
}
})
}
//其实还可以这样写
function unique(arr){
arr.forEach((item,index,arr)=>{
if(arr.indexOf(item) === index){
arr.splice(arr.indexOf(item),1);
}
})
}
//第五种Map
function unique(arr){
var newArr = [];
arr.map((item,index,arr)=>{
if(arr.indexOf(item) === index){
newArr.push(item);
}
})
}
//第六种 ES6的Set
function unique(arr){
return Array.from(new Set(arr));
}
//第七种ES6的参数增强
function unique(arr){
return [...new Set(arr)];
}
//第八种箭头函数简化
var unique = (arr) => return [...new Set(arr)];
//还有一个splice方法
function unique(arr){
for(var i = 0; i < arr.length; i ++){
for(var j = i+1; j < arr.length; j ++){
if(arr[i] === arr[j]){
arr.splice(j,1);
j --;
}
}
}
}
//哦,对了还有一个include,可以直接查找有没有某个元素
function unique(arr){
var newArr = [];
for(var i = 0; i < arr.length; i ++){
if(!newArr.include(arr[i])){
newArr.push(arr[i]);
}
}
return newArr;
}
//方法很多,其实有许多方法都是大同小异,有的简化了循环,有的简化了查找。在考察数组去重的同时要考察,数组的这些遍历方法的区别。
23.数组中forEach、filter、map、reduce、some、every的区别
-
forEach是让数组中的每一项都做一件事
arr.forEach((item,index)=>{console.log(item)});//让每一项都执行一次打印
-
map是让数组通过某种计算产生一个新的数组
var newArr = arr.map((item,index)=>{return item*2});//让每一项都乘2返回到一个新的数组中
-
filter是筛选出符合条件的项,组成一个新数组
var newArr = arr.filter((item,index)=>{return item > 3});//即只返回大于3的
-
reduce是让数组中的前一项和后一项做某种计算,并计算最终值
var arr = [1,2,3,4,5];
var result = arr.reduce((prev,next)=>{
return prev + next;
})
//即:1+2+3+4+5 = 15;
-
every检测数组中的每一项是否满足条件,返回一个布尔值
var result = arr.every((item,index)=>{return item > 0});只有所有项都满足条件,才会返回true
-
some检测数组中是否有某些满足条件
var result = arr.some((item,index)=>{return item >1});只要有一项符合,就会返回true
24.对于原型、原型链,scope、[scope chain],以及js的运行机制。在我的博客中有描述,可自行查阅。
25.写一个URL转对象的方法
function parseObj(url){
var i = url.indexOf("?");
var str = url.slice(i+1).split("&");
var obj = {};
for(var p of str ){
var prop = p.split("=");
var key = prop[0];
var val = prop[1];
obj[key]= val;
}
return obj;
}
/*------------------使用--------------------------*/
var url = "http://127.0.0.1:3000?id=1&uname=dingdingd"
var obj = parseObj(url);
console.log(obj)
补充对象转url
function parseUrl(dns,obj){
var arr = [];
for(var p in obj){
var item = p+"="+obj[p];//模板字符串 var item = `${p}=${obj[p]}`
arr.push(item);
}
return dns+"?"+arr.join("&");//模板字符串`${dns}?${arr.join("&")}`
}
/*-----------------使用----------------------*/
var obj = {
id:1,
name:'dingding'
}
var dns = "http://127.0.0.1:3000"
var url = parseUrl(dns,obj);
console.log(url);
26.数组排序
冒泡排序:
Array.prototype.bubbleSort = function(arr){
if(arr.length <= 1){ return arr}
var num = "";
for(var i = 0,len=arr.lengthl; i < len; i ++){
for(var j = 0; j < len - i - 1; j ++){
if(arr[j]>arr[j+1]){
num = arr[j];
arr[j] = arr[j+1];
arr[j+1] = num;
}
}
}
return arr;
}
快速排序
function quickSort(arr){
if(arr.length <= 1){ return arr}
var pivotIndex = Math.floor(arr.length/2);
var pivot = arr.splice(pivotIndex,1)[0];//获取基准
var left = [];
var right = [];
for(var i = 0; i < arr.length; i ++){
if(arr[i] < pivot){
left.push(arr[i])
}else{
right.push(arr[i])
}
}
return quickSort(left).concat([pivot],quickSort(right));//递归
}
插入排序
function insertSort(arr){
for(var i = 1, len = arr.length; i < len; i ++){
var item = arr[i];
var j = i - 1;
while(j >= 0 && arr[j] > item){
arr[j + 1] = arr[j];
j --; //这两步是将已排序的元素整体向前移位
}
arr[j +1] = item;//插入新元素
}
return arr;
}
选择排序
function selectSort(arr){
var tmp;
var len = arr.length;
for(var i = 0; i < len; i ++){
for(var j = i+1; j < len; j ++){
if(arr[j] < arr[i]){
tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}
}
return arr;
}
27.关于sort排序
sort排序默认是按照ASCII码进行排序的所以如果直接使用可能会不能得到理想预期的排序效果,例如:
var arr = [1,10,2,5,20,3]
arr.sort();
//结果将会是:[1, 10, 2, 20, 3, 5]
解决:sort排序接收一个函数参数,描述它的排序方式
var arr = [1,10,2,5,20,3]
arr.sort(function(a,b){
return a-b;//a-b升序,b-a为降序
});
29.web前端性能优化
- 减少http请求
- 减少DNS查询次数
- 对于必要的资源提前加载,对于不必要的延迟加载
- 减少DOM元素,并减少对DOM元素的操作。
- 根据域名划分网络,使用CDN。
- 将CSS置顶,使用link代替@import。
- 减少iframe的使用
- 使用精简的js和css代码,减小cookie的大小,减小favicon的大小。
百无一用是深情,不屑一顾是相思。
vue篇:
1.对于MVVM的理解;
model:数据模型,定义数据
view:UI组件,展示UI
viewModel:同步view和model的一个对象,处理数据,处理用户交互,控制和改变视图。
view与model之间没有直接联系。viewmodel通过双向数据绑定将view与model层连接起来,无需人为干涉,自动同步。
2.生命周期
vue用来描述组件从创建到使用再到删除的一系列过程。
四个阶段,对应八个钩子函数:
- 创建:
beforeCreate()创建前,数据监控与初始化还未开始
created()创建后,完成数据监控,属性、方法、事件、都初始化。$el属性还未显示
- 挂载:
beforeMount()挂载前,编译模板,$el和data都已经初始化,但是挂载前还是虚拟的dom节点。
mounted()挂载后,el被创建的vm的实例替换并挂载,完成渲染。
- 修改:
beforeUpdate()更新前,在数据更新前调用,在虚拟DOM重新渲染和打补丁之前。
updated(),调用时dom已经更新。尽量避免在此处进行依赖于DOM的操作,可能导致无限循环。
- 销毁:
beforeDestroy(),在实例销毁前调用,实例仍然可用
destroyed(),断开与vm实例的关联所有监听器移除,实例被销毁,dom结构依然存在,但是对数据的操作将不在影响到视图。
3.第一次页面加载会触发那些钩子函数
beforeCreat,created,beforeMount,mounted
4.DOM渲染在那个周期中完成
DOM渲染在mounted中就已经完成。
5.vue双向绑定的原理
通过Object.definedProperty劫持各个属性的访问器,在数据变动时发布消息给订阅者,触发相应的监听回调。
步骤:
1.对需要被观察的数据对象进行递归遍历,包括子属性对象上的属性。都添加上getter、setter、,这样对某个属性赋值,就能触发setter,就可以监听数据变化。
2.compile解析模板指令,将模板中的变量替换成数据,并对每个绑定更新的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,就会收到通知,从而更新视图。
3.watcher订阅者作为observe和compile之间的通讯桥梁。
在实例化时向属性订阅器里添加自身。
自身要定义一个update方法,在属性变动时收到通知,调用自身的update方法,然后触发compile中绑定的回调。
4.最后MVVM作为数据绑定的入口,结合observe、compile、watcher,通过observe监听变化,通过compile编译解析模板指令,再利用watcher搭建的通信桥梁,达到数据变化–>到试图更新,视图交互–>数据变更的双向绑定
再谈谈对template编译的理解:
通过compile编译器将template编译成AST,然后AST经过genereate,转换为render函数,render的返回值正是vue的虚拟DOM节点,VNODE
简单实现一个双向绑定
Object.defineProperty劫持数据,实现双向绑定
<input type="text" id="myVal">
<p id="text"></p>
var obj = {};
Object.defineProperty(obj,"info",{
get:function(){
console.log("info属性被访问");
}
set:function(newVal){
console.log("info属性被修改");
document.getElementById("myVal").value = newVal;
documnet.getElementById("text").innerHTML = newVal;
}
});
document.getElementById("myVal").addEventListener("input",function(e){
obj.info = e.target.value;
});
Proxy劫持数据,实现双向绑定
var obj = {};
var newObj = Proxy(obj,{//代理
get:function(target,key,reciver){
return Reflect.get(target,key,reciver);//反射
},
set:function(target,key,value,reciver){
document.getElementById("myVal").value = value;
documnet.getElementById("text").innerHTML = value;
return Reflect.set(target,key,value,reciver);
}
});
document.getElementById("myVal").addEventListener('keyup',function(e){
newObj.info = e.target.value;
})
6.组件间传参
父子组件:
props-down,event-up
兄弟间:
可以通过父子的方式传递,较为复杂。
也可以通过vue-bus,实现一个数据总线。
也可以VUEX
7.Vue的路由实现
hash模式:即浏览器地址栏的#,以及#后面的字符就是hash,可以用window.location.hash读取,hash只是用来指导浏览器动作,不会重加载页面,不包括在http请求中,对服务端安全。
8.vue路由的钩子函数
首页可以控制导航跳转,beforeEach,afterEach等,一般用于页面title的修改,和一些登录时重定向功能。
boforeEach三个参数to,from,next,
to:要跳转到的路由
from:当前要离开的路由
next:函数,必须调用该方法的resolve这个钩子函数。
9.vuex是什么,怎么用,什么场景用。
vuex是vue生态系统中的状态管理。
在main.js中引入store,
store对象的state中,定义要管理的状态数据
getter用于访问state中的状态数据,想当与computed,
mutation中定义操作状态数据的方法,若要执行,此处只能写同步方法,this.$store.commit(“函数名”,参数);
action:定义行为,可以将mutations中定义的方法,变成可异步处理数据的方法,this.$store.dispath(“函数名”,参数);
modules:在项目复杂时,进一步细分模块,使其各自管理各自的状态数据以及方法的定义。
10.vue3.0与vue2.x的比较
虽然vue3.0还未正式发布,但是其热度持续高涨,上个月还通过远程视频展示了vue3.0的进展,以及使用的新技术。
视频中展示到的vue3.0的更新改动。
- 模板编译部分:
vue3.0重构了virtualDOM,性能最高可翻一倍。
从运行时判断,到编译时判断,对于原生html直接使用原生,对于组件再进行渲染。以减少开销
优化slots生成
静态内容提取
- 数据监听部分:
基于Proxy的监听,相比较Object.defineProperty减小开销,并且监听方法更多。
按需引入
由Flow迁移到TypeScript,
重构模板编译器
内存占用减半,速度加倍。
一些小点但也很重要:
css如何仅用于当前组件:在style标签上添加:scoped
v-if和v-show的区别:v-if是判断是否渲染,要么渲染要么删除。v-show是控制display属性是block或none。
$route和$router的区别:
$route是路由信息对象,包含各种路由信息参数。
$router是当前的路由实例对象,包括跳转方法和钩子函数。
如何搭建脚手架:
(1)下载vue-cli工具到当前计算机,全局安装
npm install -g @vue/cli
(2)运行cli工具,创建一个空白脚手架
vue create xz_admin_v2
若要在微信小程序中可以使用支持vue的框架,可以使用美团点评团队出品的小程序开发框架mpvue,原生支持vue。
vue init mpvue/mpvue-quickstart my-project
(3)运行
npm run serve
(4)浏览器访问
http://127.0.0.1:8080一般默认在此端口,也可能不一样
<keep-alive></keep-alive>,包裹动态组件时,会缓存不活动的组件实例,主要用于保留组件状态或者避免重新渲染。
<noscript>标签的作用
最初目的是帮助老旧浏览器的平滑升级更替,因为早期的浏览器并不能支持 JavaScript。noscript 标签在不支持JavaScript 的浏览器中显示替代的内容。这个元素可以包含任何 HTML 元素。这个标签的用法也非常简单:
例如:
<noscript>
<p>本页面需要浏览器支持(启用)JavaScript</p>
</noscript>
浏览器之间为什么会有兼容性问题?
首先一点:各浏览器厂商自身的技术标准,对于W3C标准的实现方式不同。
其次:某些浏览器先W3C标准一步实现了一些标准,并且自己定义了一些标准。
最后在基于各自浏览器的内核不同对于解析就会有出入等原因。
你想遇到过那些浏览器的兼容性问题:
就目前阶段的个人见解:所遇到的基本是IE与Chrome的兼容问题。可以分html、css、js三个部分,
html部分:例如H5中的新特性,某些新标签等,无法被低版本的IE识别。
css部分:就这个最烦!一些网页你写好后在chrome上很漂亮,到了IE简直了。说一说我所遇到的一些问题(仅针对IE9以下):
1.低版本的盒模型计算方式问题。同样的元素宽度,在两者之间就会产生不同的结果,到了IE下就会缩小。
2.元素选择器,一些元素选择器在IE中没法使用。应该是没有实现的原因。
3.一些样式在IE中与在chrome中的渲染方式不同。
4.浮动元素的双边距bug,怎么改,设置这些元素的display:inline。
js部分:也有许多问题
1.获取事件的方式不同,IE是window.event,chrome是函数的参数e。以及获取事件目标元素的方式也不同。IE是e.srcElement,chrome是e.target。
2.绑定事件处理函数的不同,chrome中一般用addeventListnenr,IE用attachEvent.参数也不同
3.事件处理机制不同,IE是冒泡机制,标准是捕获机制,后来标准中也加入了冒泡机制,也就是addEventListener的第三个参数的由来。
4.ajax核心对象,chrome用XMLHttpRequest对象,IE用ActiveXObject,而且参数还有不同。
目前遇到的兼容性问题就这些了。