它是一种异步通信的方法,通过直接由 js 脚本向服务器发起 http 通信,然后根据服务器返回的数据,更新网页的相应部分,而不用刷新整个页面的一种方法。
面试手写(原生):
//1:创建Ajax对象
var xhr = window.XMLHttpRequest?new XMLHttpRequest():new ActiveXObject('Microsoft.XMLHTTP');// 兼容IE6及以下版本
//2:配置 Ajax请求地址
xhr.open('get','index.xml',true);
//3:发送请求
xhr.send(null); // 严谨写法
//4:监听请求,接受响应
xhr.onreadysatechange=function(){
if(xhr.readySate==4&&xhr.status==200 || xhr.status==304 )
console.log(xhr.responsetXML)
}
jQuery写法
$.ajax({
type:'post',
url:'',
async:ture,//async 异步 sync 同步
data:data,//针对post请求
dataType:'jsonp',
success:function (msg) {
},
error:function (error) {
}
})
promise 封装实现:
// promise 封装实现:
function getJSON(url) {
// 创建一个 promise 对象
let promise = new Promise(function(resolve, reject) {
let xhr = new XMLHttpRequest();
// 新建一个 http 请求
xhr.open("GET", url, true);
// 设置状态的监听函数
xhr.onreadystatechange = function() {
if (this.readyState !== 4) return;
// 当请求成功或失败时,改变 promise 的状态
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
};
// 设置错误监听函数
xhr.onerror = function() {
reject(new Error(this.statusText));
};
// 设置响应的数据类型
xhr.responseType = "json";
// 设置请求头信息
xhr.setRequestHeader("Accept", "application/json");
// 发送 http 请求
xhr.send(null);
});
return promise;
}
function flat11(arr) {
var res = [];
for (var i = 0; i < arr.length; i++) {
if (Array.isArray(arr[i])) {
res = res.concat(flat11(arr[i]));
} else {
res.push(arr[i]);
}
}
return res;
}
如果想实现第二个参数(指定“拉平”的层数),可以这样实现,后面的几种可以自己类似实现:
function flat(arr, level = 1) {
var res = [];
for(var i = 0; i < arr.length; i++) {
if(Array.isArray(arr[i]) || level >= 1) {
res = res.concat(flat(arr[i]), level - 1);
}
else {
res.push(arr[i]);
}
}
return res;
}
function flat(arr) {
return arr.reduce(
(pre, cur) => pre.concat(Array.isArray(cur) ? flat(cur) : cur), []
);
}
ES6 的扩展运算符(...) 只能扁平化一层
function flat(arr) {
return [].concat(...arr);
}
全部扁平化:遍历原数组,若arr
中含有数组则使用一次扩展运算符,直至没有为止。
function flat(arr) {
while(arr.some(item => Array.isArray(item))) {
arr = [].concat(...arr);
}
return arr;
}
调用数组的 toString()/join()
方法(它会自动扁平化处理),将数组变为字符串然后再用 split
分割还原为数组。由于 split
分割后形成的数组的每一项值为字符串,所以需要用一个map
方法遍历数组将其每一项转换为数值型。
function flat(arr){
return arr.toString().split(',').map(item => Number(item));
// return arr.join().split(',').map(item => Number(item));
}
JSON.stringify(arr).replace(/[|]/g, '')
会先将数组arr
序列化为字符串,然后使用 replace()
方法将字符串中所有的[
或 ]
替换成空字符,从而达到扁平化处理,此时的结果为 arr
不包含 []
的字符串。最后通过JSON.parse()
解析字符串。
function flat(arr) {
return JSON.parse("[" + JSON.stringify(arr).replace(/\[|\]/g,'') + "]");
}
类数组是具有 length
属性,但不具有数组原型上的方法。常见的类数组有 arguments
、DOM 操作方法返回的结果(如document.querySelectorAll('div')
)等。
注意:扩展运算符只能作用于 iterable
对象,即拥有 Symbol(Symbol.iterator)
属性值。
let arr = [...arrayLike]
let arr = Array.from(arrayLike);
let arr = Array.prototype.slice.call(arrayLike);
let arr = Array.apply(null, arrayLike);
let arr = Array.prototype.concat.apply([], arrayLike);
async function fn(){
try{
let a = await Promise.reject('error')
}catch(error){
console.log(error)
}
}
响应式网站设计(Responsive Web design
)是一个网站能够兼容多个终端,而不是为每一个终端做一个特定的版本。
关于原理: 基本原理是通过媒体查询(@media)
查询检测不同的设备屏幕尺寸做处理。
关于兼容: 页面头部必须有mate声明的viewport
。
<meta name="’viewport’" content="”width=device-width," initial-scale="1." maximum-scale="1,user-scalable=no”"/>
他们都是 CSS 预处理器,是 CSS 上的一种抽象层。他们是一种特殊的语法/语言编译成 CSS。 例如 Less 是一种动态样式语言,将 CSS 赋予了动态语言的特性,如变量,继承,运算, 函数,LESS 既可以在客户端上运行 (支持 IE 6+, Webkit, Firefox),也可以在服务端运行 (借助 Node.js)。
为什么要使用它们?
通过修改某个属性值呈现的内容就可以被替换的元素就称为“替换元素”。
替换元素除了内容可替换这一特性以外,还有以下特性:
替换元素的尺寸从内而外分为三类:
这三层结构的计算规则具体如下:
(1)如果没有CSS尺寸和HTML尺寸,则使用固有尺寸作为最终的宽高。
(2)如果没有CSS尺寸,则使用HTML尺寸作为最终的宽高。
(3)如果有CSS尺寸,则最终尺寸由CSS属性决定。
(4)如果“固有尺寸”含有固有的宽高比例,同时仅设置了宽度或仅设置了高度,则元素依然按照固有的宽高比例显示。
(5)如果上面的条件都不符合,则最终宽度表现为300像素,高度为150像素。
(6)内联替换元素和块级替换元素使用上面同一套尺寸计算规则。
参考 前端进阶面试题详细解答
async function async1() {
console.log("async1 start");
await async2();
console.log("async1 end");
setTimeout(() => {
console.log('timer1')
}, 0)
}
async function async2() {
setTimeout(() => {
console.log('timer2')
}, 0)
console.log("async2");
}
async1();
setTimeout(() => {
console.log('timer3')
}, 0)
console.log("start")
输出结果如下:
async1 start
async2
start
async1 end
timer2
timer3
timer1
代码的执行过程如下:
async1
,打印出async1 start
;async2
,进入async2
,遇到定时器timer2
,加入宏任务队列,之后打印async2
;async2
阻塞了后面代码的执行,所以执行后面的定时器timer3
,将其加入宏任务队列,之后打印start
;async1 end
,遇到定时器timer1,将其加入宏任务队列;timer2
,timer3
,timer1
,没有微任务,所以直接所有的宏任务按照先进先出的原则执行。解构是 ES6 提供的一种新的提取数据的模式,这种模式能够从对象或数组里有针对性地拿到想要的数值。 1)数组的解构 在解构数组时,以元素的位置为匹配条件来提取想要的数据的:
const [a, b, c] = [1, 2, 3]
最终,a、b、c分别被赋予了数组第0、1、2个索引位的值:
数组里的0、1、2索引位的元素值,精准地被映射到了左侧的第0、1、2个变量里去,这就是数组解构的工作模式。还可以通过给左侧变量数组设置空占位的方式,实现对数组中某几个元素的精准提取:
const [a,,c] = [1,2,3]
通过把中间位留空,可以顺利地把数组第一位和最后一位的值赋给 a、c 两个变量:
2)对象的解构 对象解构比数组结构稍微复杂一些,也更显强大。在解构对象时,是以属性的名称为匹配条件,来提取想要的数据的。现在定义一个对象:
const stu = {
name: 'Bob',
age: 24
}
假如想要解构它的两个自有属性,可以这样:
const { name, age } = stu
这样就得到了 name 和 age 两个和 stu 平级的变量:
注意,对象解构严格以属性名作为定位依据,所以就算调换了 name 和 age 的位置,结果也是一样的:
const { age, name } = stu
一般两栏布局指的是左边一栏宽度固定,右边一栏宽度自适应,两栏布局的具体实现:
.outer {
height: 100px;
}
.left {
float: left;
width: 200px;
background: tomato;
}
.right {
margin-left: 200px;
width: auto;
background: gold;
}
.left{
width: 100px;
height: 200px;
background: red;
float: left;
}
.right{
height: 300px;
background: blue;
overflow: hidden;
}
.outer {
display: flex;
height: 100px;
}
.left {
width: 200px;
background: tomato;
}
.right {
flex: 1;
background: gold;
}
.outer {
position: relative;
height: 100px;
}
.left {
position: absolute;
width: 200px;
height: 100px;
background: tomato;
}
.right {
margin-left: 200px;
background: gold;
}
.outer {
position: relative;
height: 100px;
}
.left {
width: 200px;
background: tomato;
}
.right {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 200px;
background: gold;
}
产生乱码的原因:
gbk
的编码,而内容中的中文字是utf-8
编码的,这样浏览器打开即会出现html
乱码,反之也会出现乱码;html
网页编码是gbk
,而程序从数据库中调出呈现是utf-8
编码的内容也会造成编码乱码;解决办法:
gbk
,而数据库储存数据编码格式是UTF-8
,此时需要程序查询数据库数据显示数据前进程序转码;.square {
width: 10%;
height: 10vw;
background: tomato;
}
.square {
width: 20%;
height: 0;
padding-top: 20%;
background: orange;
}
.square {
width: 30%;
overflow: hidden;
background: yellow;
}
.square::after {
content: '';
display: block;
margin-top: 100%;
}
function Foo(){
Foo.a = function(){
console.log(1);
}
this.a = function(){
console.log(2)
}
}
Foo.prototype.a = function(){
console.log(3);
}
Foo.a = function(){
console.log(4);
}
Foo.a();
let obj = new Foo();
obj.a();
Foo.a();
输出结果:4 2 1
解析:
是基于原型的动态语言,主要独特特性有 this、原型和原型链。
JS 严格意义上来说分为:语言标准部分(ECMAScript)+ 宿主环境部分
语言标准部分
2015 年发布 ES6,引入诸多新特性使得能够编写大型项目变成可能,标准自 2015 之后以年号代号,每年一更
宿主环境部分
当渲染树中部分或者全部元素的尺寸、结构或者属性发生变化时,浏览器会重新渲染部分或者全部文档的过程就称为回流。
下面这些操作会导致回流:
在触发回流(重排)的时候,由于浏览器渲染页面是基于流式布局的,所以当触发回流时,会导致周围的DOM元素重新排列,它的影响范围有两种:
当页面中某些元素的样式发生变化,但是不会影响其在文档流中的位置时,浏览器就会对元素进行重新绘制,这个过程就是重绘。
下面这些操作会导致回流:
注意: 当触发回流时,一定会触发重绘,但是重绘不一定会引发回流。
margin
正值时,可以让margin
使用负值解决;font-size
时,可通过设置font-size:0
、letter-spacing
、word-spacing
解决;冒泡和捕获
三个方面来说明前端性能优化
一: webapck优化与开启gzip压缩
1.babel-loader用 include 或 exclude 来帮我们避免不必要的转译,不转译node_moudules中的js文件
其次在缓存当前转译的js文件,设置loader: 'babel-loader?cacheDirectory=true' 2.文件采用按需加载等等 3.具体的做法非常简单,只需要你在你的 request headers 中加上这么一句: accept-encoding:gzip 4.图片优化,采用svg图片或者字体图标 5.浏览器缓存机制,它又分为强缓存和协商缓存二:本地存储——从 Cookie 到 Web Storage、IndexedDB 说明一下SessionStorage和localStorage还有cookie的区别和优缺点三:代码优化 1.事件代理 2.事件的节流和防抖 3.页面的回流和重绘 4.EventLoop事件循环机制 5.代码优化等等
防抖(debounce
):触发高频事件 N 秒后只会执行一次,如果 N 秒内事件再次触发,则会重新计时。类似王者荣耀的回城功能,你反复触发回城功能,那么只认最后一次,从最后一次触发开始计时。
核心思想:每次事件触发就清除原来的定时器,建立新的定时器。使用apply或call调用传入的函数。函数内部支持使用 this 和 event 对象;
应用:防抖常应用于用户进行搜索输入节约请求资源,window
触发resize
事件时进行防抖只触发一次。
实现:
function debounce(fn, delay) {
// 利用闭包的原理
let timer = null;
return function(...args){
if(timer) clearTimeout(timer);
timer = setTimeout(() => {
// 改变 this 指向为调用 debounce 所指的对象
fn.call(this, ...args);
// fn.apply(this, args);
}, delay);
}
}
1、意外的全局变量:由于使用未声明的变量,而意外的创建了一个全局变量,而使这个变量一直留在内存中无法被回收
2、被遗忘的计时器或回调函数:设置了 setInterval 定时器,而忘记取消它,如果循环函数有对外部变量的引用的话,那么这个变量会被一直留在内存中,而无法被回收。
3、脱离 DOM 的引用:获取一个 DOM 元素的引用,而后面这个元素被删除,由于一直保留了对这个元素的引用,所以它也无法被回收。
4、闭包:不合理的使用闭包,从而导致某些变量一直被留在内存当中。
// call
Function.prototype.call = function (context, ...args) {
context = context || window;
const fnSymbol = Symbol("fn");
context[fnSymbol] = this;
context[fnSymbol](...args);
delete context[fnSymbol];
}
// apply
Function.prototype.apply = function (context, argsArr) {
context = context || window;
const fnSymbol = Symbol("fn");
context[fnSymbol] = this;
context[fnSymbol](...argsArr);
delete context[fnSymbol];
}
// bind
Function.prototype.bind = function (context, ...args) {
context = context || window;
const fnSymbol = Symbol("fn");
context[fnSymbol] = this;
return function (..._args) {
args = args.concat(_args);
context[fnSymbol](...args);
delete context[fnSymbol];
}
}
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。