JavaScript执行上下文(context)主要指代码执行环境的抽象概念。执行上下文分为三种:
### 每一段js代码执行,都会先创建一个上下文环境。
前沿知识:js代码执行前会创建上下文环境,这个上下文环境包含了变量、作用域链和this.
简单理解就是从当前环境向父级一层一层查找变量的过程称之为作用域链。
var name = '前端未来';
function hello(){
console.log(name);
}
###解释:当我们在函数hello里面打印name的时候,会先在hello作用域中查找,如果没有找到就去hello的父级作用域中查找。
每个函数都拥有一个prototype属性,每个函数实例对象都拥有一个__proto__属性,而这个属性指向了函数的prototype,当我们访问实例对象的属性或者方法时,会先从自身构造函数中查找,如果没有就通过__proto__去原型中查找,这个查找的过程我们称之为原型链。(跟作用域链有点像)
// 定义动物 - 父类
function Animal(){
this.age = 10;
this.say = function(){
return 'hello tom';
}
}
// 定义猫 - 子类
function Cat(){
this.name = 'tom';
}
// 通过原型继承动物类
Cat.prototype = new Animal()
// 实例化一个cat对象
var cat = new Cat();
// 打印返回true
cat.__proto__ === Cat.prototype
// 打印age,会先查找cat,再查找Animal
console.log(cat.age)
###通过截图,我们可以看到cat实例对象__proto__指向了Animal,当cat没有age的时候,会通过__proto__到原型上查找,如果原型上依然没有,会继续向Object上查找。
// 正常访问
var lan = 'zh';
function hello(){
var name = '前端未来';
}
console.log(name)//很明显'undefined'
// 换成闭包
function hello(){
var name = '前端未来';
function demo(){
console.log(name)//打印:前端未来
}
}
###以上几个基础面试题都是密切关联的,理解其中一个,基本就能理解剩下几个
JS数据类型分别基本数据类型和引用数据类型,基本数据类型保存的是值,引用类型保存的是引用地址(this指针)。浅拷贝共用一个引用地址,深拷贝会创建新的内存地址。
浅拷贝方法
深拷贝
面试官希望的答案:Object.prototype.toString.call([]) 返回 "[object Array]"
扩展答案
###数组也是引用类型,通过typeof依然返回object
这个非常多,说起来也很快,我主要考察你会多少,另外也为了引出下一个问题,slice和splice区别
push 末尾添加
pop 末尾删除
shift 首部删除
unshift 首部添加
concat 数组合并
join 数组元素 通过连接符 连接
reverse 数组反转
sort 数组排序
map/forEach/filter/indexOf/includes/slice/splice
slice表示截取,slice(start,end)不改变原数组,返回新数组。
splice表示删除,splice(start,length,item),会改变原数组,从某个位置开始删除多个元素,并可以插入新的元素。
创建节点
修改节点
!import > 内联样式(style) > ID选择器 > 类/属性/伪类 > 元素/关系
.left{ width:200px; 或者 flex:0 0 200px; }
.right{ width:200px; 或者 flex:0 0 200px; }
.center{ flex:1; }
### 阿里圣杯布局(忽略)
flex是一种弹性布局,包含flex-container和flex-item.
常用的属性包括flex-direction、flex-wrap、justify-content、align-items
水平居中 justify-content:center
水平两头居中 justify-content:space-between
垂直居中 align-items:center
盒模型包括:content,padding,border,margin
盒模型分为:IE盒模型和标准w3c盒模型
###IE盒模型宽度包含了padding和border,w3c盒模型宽度就是内容宽度。
他们虽然都可以做出动画效果,但是transition主要做简单的过渡效果,而animation可以做复杂的动画效果,在语法和用法上有非常大的区别。
H5自适应方案大家在网速能找到很多,我个人推荐一种我非常喜欢的方式,就是rem. rem是一种相对单位,它基于html的font-size值来进行调整。
通常我们以750为基准,我们会在header中嵌套一段js脚本,获取手机网页分辨率尺寸除以375,为了方便计算,我们假设750像素下1rem = 100px;所以 我们除以375后需要乘以50.
他们都可以改变函数的作用域。
call/apply可以直接执行该函数,而bind不会立刻执行
call/apply作用类似,都可以改变指针和执行函数,区别在于传参不同,call需要单个传参,apply通过数组传参
这其中大家需要了解几个概念:调用栈、同步/异步任务、宏任务/微任务
JavaScript本身是单线程,也就是同一时刻只能干一件事,JS任务包含了同步任务和异步任务,遇到执行函数会将其放入调用栈(先进后出)中,遇到setTimeout/setInterval等异步任务时,会把它放入到消息队列中,等主线程的任务执行完成以后,再回过头执行消息队列中的异步任务,如果异步任务中仍然有异步任务,会继续放入消息队列,以此类推,便形成了一个事件循环。
异步任务:
setTimeout
setInterval
异步任务又分为宏任务和微任务,promise就属于微任务.
1.大小方面
GET传输一般2K-8K,IE限制2K,,POST没有大小限制
1、安全方面
GET通过url明文传输,POST通过body传输,本身都不安全,因为HTTP就是明文传输。
3.浏览器记录
GET请求浏览器会记录,POST不会
4.浏览器后退
GET无害,POST会再次提交
5.浏览器收藏
GET可以收藏,POST不可以
6.浏览器缓存
GET可以缓存,POST不会
7.编码方式
GET通过url编码,POST支持多种编码
8.TCP数据包
GET产生一个数据包,POST产生2个数据包
9.使用方式(习惯上讲)
GET主要拉取数据,POST主要提交保存数据
防抖和节流都是希望在同一时间内,不要重复触发请求。一般场景用在搜索和网页滚动事件中。
区别:
防抖主要是在规定时间内只触发一次,如果再次调用,时间从新计算。(只要在规定时间内重复触发函数,该函数就不会再次生效)
//模拟一段ajax请求
function ajax(content) {
console.log('ajax request ' + content)
}
function debounce(fun, delay) {
return function (args) {
let that = this
let _args = args
clearTimeout(fun.id)
fun.id = setTimeout(function () {
fun.call(that, _args)
}, delay)
}
}
let inputb = document.getElementById('debounce')
let debounceAjax = debounce(ajax, 500)
inputb.addEventListener('keyup', function (e) {
debounceAjax(e.target.value)
})
节流主要是在固定时间内只触发一次。比如每间隔1秒触发一次。(不管在规定时间内触发多少次,在规定间隔时间中只触发一次)
function throttle(fun, delay) {
let last, deferTimer
return function (args) {
let that = this
let _args = arguments
let now = +new Date()
if (last && now < last + delay) {
clearTimeout(deferTimer)
deferTimer = setTimeout(function () {
last = now
fun.apply(that, _args)
}, delay)
}else {
last = now
fun.apply(that,_args)
}
}
}
let throttleAjax = throttle(ajax, 1000)
let inputc = document.getElementById('throttle')
inputc.addEventListener('keyup', function(e) {
throttleAjax(e.target.value)
})
let arr = [1,2,3,4,5,1,2]
let set = new set(arr)
console.log(set) //set {1,2,3,4,5}
// 速度最快, 占空间最多(空间换时间)
function noReapet2(array){
//定义一个空对象,空数组
var obj= {}, arr = [], len = array.length, val, type;
for (var i = 0; i < array.length; i++) {//循环遍历原数组所有元素
//将原数组中第i个元素赋值给val
val = array[i];
type = typeof val;
if (!obj[val]) {//判断对象中是否有该元素,如果没有
obj[val] = [type];
arr.push(val);
} else if (obj[val].indexOf(type) < 0) {
obj[val].push(type);
arr.push(val);
}
}
return arr;
}
function noReapet(arry){
var arr = [] ;
for(var i = 0 , l = arry.length ; i < l ; i++){
for(var j = i+1 ; j< l ; j++){
if(arry[i] == arry[j]){
j = ++i
arr.push(arr[i])
}
}
}
return arr
}
function noReapet1(arry){
//定义一个新数组,用于存放去重的结果
var arr = [];
for(let i = 0 ; i < arry.length ; i++){
//如果当前数组中第i个元素已经保存到临时数组,那么跳过
//否则将该元素存入到临时数组中
if ( arr.indexOf( arry[i]) == -1)//indexOf()方法,检索字符串中的值是否出现,没出现返回-1
arr.push( arry.[i])
}
return arr;
}
//判断浏览器是否支持indexOf,indexOf是es5的新方法,IE8及以下版本不支持该方法
if( !Arry.prototype.indexOf){
//不支持的情况下,为其增加indexOf方法
Arry.prototype.indexOf = function(item){
var result = -1, a_irem = null;
if( this.length == 0){//当数组为空时,直接返回-1
return result;
}
for( var i = 0 ; i <this.length ; i++){
//将数组中第I个元素赋值给a_item,
a_item = this[i];
if( a_item === item){//如果item等于这个元素,说明数组中有这个元素,返回一个正值
return = i;
break;
}
}
return result;
}
}
// 将相同的值相邻,然后遍历去除重复值
function noReapet(array){
array.sort(); //将数组进行排序
var re=[array[0]];
for(var i = 1; i < array.length; i++){
if( array[i] !== re[re.length - 1])
{
re.push(array[i]);
}
}
return re;
}
数组sort排序
冒泡排序(两层循环,两两互换)
//冒泡排序
function bubbleSort ( data ) {
var temp = 0;
for ( var i = data.length ; i > 0 ; i -- ){
for( var j = 0 ; j < i - 1 ; j++){
if( data[j] > data[j + 1] ){
temp = data[j];
data[j] = data [j+1];
data[j+1] = temp;
}
}
}
return data;
}
选择排序
//选择排序
function selectionSort( data ) {
for( var i = 0; i< data.length ; i++){
var min = data[i];
var temp;
var index = i;
for( var j = i + 1; j< data.length; j++){
if( data[j] < min ){
min = data[j];
index = j;
}
}
temp = data[i];
data[i] = min;
data[index]= temp;
}
return data;
}
插入排序
//插入排序
function insertionSort( data ) {
var len = data.length;
for (var i = 1; i < len; i++) {
var key = data[i];
var j = i - 1;
while ( j >= 0 && data[j] > key) {
data[j + 1] = data[j];
j--;
}
data[j + 1] = key;
}
return data;
}
在Vue中通过观察者模式触发视图更新。Vue2.x通过Object.defineProperty劫持data数据,当数据变化后触发setter,setter内部通过订阅器来notify消息,notify会调用watcher更新视图。
当一套前端对接不同后端服务时,会出现数据解构不一致情况,这个时候可以使用适配器模式来兼容不同后端,使他以统一的数据解构对接前端。
for…of 是ES2015版本引入的语法,它可以遍历数组、类数组、以及Map/set/字符串等
for (const number of [1,2,5]) { }
for (const number ofarguments) { }
const message = 'hello';for (const character of message) { }
const map = new Map();
map.set("name", '前端未来');
map.set("author", '河畔一角');
for(const item ofmap){ } // 或者
for(const [key,val] of map){ }
const names = new
Set(['Tom', 'Jack', 'Lily']); for (let name of names) { }
for…of非常灵活,还有其它例子,我这儿不再列出。
跨域是浏览器做出的安全限制,必须同协议、同域名、同端口否则会被浏览器block
优化策略:减少请求次数、减小资源大小、提高响应和加载速度、优化资源加载时机、优化加载方式