web前端面试题之魂(js)

JS基础

变量

面试题

1、JS使用typeof能得到哪些类型?

考点: JS变量类型

typeof undefined, //undefined
typeof 'abc' ,//string
typeof 123 ,//number
typeof true ,//boolean
typeof {} ,//object
typeof [] ,//object
typeof null ,//object
typeof console.log ,//function
typeof Symbol(1) // symbol 

2、何时使用 === 何时使用 == ?

考点: 强类型转换

if(obj.a == null){
    //这里相当于 obj.a === null || obj.a === undefined,简写形式
    //这是 jquery 源码中推荐的写法
}

3、JS中有哪些内置函数?

ObjectArray、Boolean、NumberString、Function、DateRegExp、Error、MathJSON、window、document等。。

4、JS变量按照存储方式区分哪些类型,并描述其特点

//值类型
//占用空间固定,值保存在栈中
//保存与复制的是值本身
var a = 10;
var b = a;
a = 11
console.log(b) //10

//引用类型
//占用空间不固定,值保存在堆中
//保存与复制的是指向对象的一个指针
var obj1 = {x: 100};
var obj2 = obj1;
obj1.x = 200;
console.log(obj2.x) //200

5、如何理解JSON?

JSON 只不过是一个 JS 对象而已

JSON.stringify({a:10, b:20})

JSON.parse('{"a": 10, "b": 20 }')

变量类型

——链接

  • 值类型(内存中存具体值) vs 引用类型(内存中存的指针)

    • 引用类型:对象,数组,函数

    • 引用类型特点:不限制扩张属性

    • 因为引用类型占据空间较大,所以用指针来共享空间

  • typeof运算符详解

    • typeof只能区分值类型的详细类型
  • 使用instanceof判断变量的详细类型

    • arr instanceof Array; //true
  • 使用Object.prototype.toString判断。

    • 数值:返回[object Number]。
    • 字符串:返回[object String]。
    • 布尔值:返回[object Boolean]。
    • undefined:返回[object Undefined]。
    • null:返回[object Null]。
    • 数组:返回[object Array]。
    • arguments 对象:返回[object Arguments]。
    • 函数:返回[object Function]。
    • Error 对象:返回[object Error]。
    • Date 对象:返回[object Date]。
    • RegExp 对象:返回[object RegExp]。
    • 其他对象:返回[object Object]。

变量计算

  • 强制类型转换(值类型)

    • 字符串拼接

    • == 运算符

    • 使用 == 的情况,除了以下情况,其他一律用 ===

      • 查看对象的属性是否存在 》if (obj.a == null){ }

      • 查看函数中的参数是否存在 》function(a,b) {if (a == null) {...} }

        var obj = {};
        if(obj.a == null){
            //这里相当于 obj.a === null || obj.a === undefined,简写形式
            //这是 jquery 源码中推荐的写法
        }
        
    • if语句

    • 逻辑运算 (&&、||、!(not))

    • 0、NaN、’’、""、null、undefined、false 自动转换为false

    //字符串拼接
    var a = 100 + 10 //110
    var b = 100 + '10' //"10010"
    // == 运算符
    100 == '100' //true
    0 == '' //true
    //if语句
    if(100) {} //把数字转换为true
    if('') {} //把字符串转换为false
    //逻辑运算
    console.log(10&&0) //0 把10转换为true
    console.log(''||'abc') // "abc" 把‘’转换为false
    console.log(!window.abc)//true window.abc是undefined,把非undefined 转换 为true
    //判断一个变量会被当做true还是false
    var a = 100
    console.log(!!a); // true
    

原型 原型链

面试题

1、如何准确判断一个变量是数组类型

变量 instanceof Array

var arr = [];
arr instanceof Array //true
typeof arr //"object", typeof 是无法判断是否是数组的

2、写一个原型链继承的例子

//写一个封装DOM的例子
    function Elem(id){
        this.elem = document.getElementById(id);
    }
    Elem.prototype.html = function (val) {
        var elem = this.elem;
        if(val){
            elem.innerHTML = val;
            return this;  //链式操作, 可有可无
        }else{
            return elem.innerHTML;
        }
    }
    Elem.prototype.on = function(type, fn){
        var elem = this.elem;
        elem.addEventListener(type, fn);
        return this;
    }
    var div1 = new Elem('div1');
    //console.log(div1.html())
    div1.html('<p>hello world<p>').on('click', function(){
        alert('clicked');
    }).html('<p>javascript<p>'

3、描述 new 一个对象的过程

  • 创建一个新对象
  • 将构造函数的作用域赋给新对象(this指向这个新对象)
  • 执行构造函数中的代码,即对this赋值
  • 返回新对象,即返回this
function Foo(name, age) {
    this.name = name
    this.age = age
    //return this;
}
var f = new Foo('zhangsan', age)

4、zepto(或其他框架)源码中如何使用原型链

构造函数

//基本碰到首字母大写的函数, 就是构造函数,so构造函数尽量大写字母开头
function Foo(name, age) {
    this.name = name
    this.age = age
    this.class = 'class-1'
    //return this //默认有这一行
}
var f = new Foo('zhangsan', 20)
//var f1 = new Foo('lisi', 22) //创建多个对象
构造函数——扩展
  • var a = {} 其实是 var a = new Object() 的语法糖
  • var a = [] 其实是 var a = new Array() 的语法糖
  • function Foo() {…} 其实是 var Foo = new Function(…)
  • 使用 instanceof 判断一个函数是否是一个变量的构造函数

原型规则和实例

5条原型规则
  1. 所有的引用类型(数组、对象、函数),都具有对象的特性、即可自由扩展属性(除了 “null” 意外)

  2. 所有的引用类型(数组、对象、函数),都有一个__proto__(隐式原型) 属性,属性值是一个普通的对象

  3. 所有的函数,都有一个prototype(显示原型) 属性, 属性值也是一个普通的对象

  4. 所有引用类型(数组、对象、函数),__proto__ 属性值都指向它的构造函数的 prototype属性值

  5. 当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么会去它的__proto__(即它的构造函数的prototype) 中寻找。

var obj = {}; obj.a = 100; //1
var arr = []; arr.a = 100;
function Fn() {}
Fn.a = 100;

console.log(obj.__proto__);//2
console.log(arr.__proto__);
console.log(Fn.__proto__);

console.log(Fn.prototype);//3
console.log(obj.__proto__ === Object.prototype)//4 5
//构造函数
function Foo(name, age) {
    this.name = name;
}
Foo.prototype.alertName = function() {
    alert(this.name);
}
//创建示例
var f = new Foo('zhangsan')
f.printName = function() {
    console.log(this.name)
}
//无论是函数f自身属性,还是从原型得到的属性,this永远指向f自己本身
f.printName();
f.alertName();
f.toString(); //要去 f.__proto__.__proto__ 寻找

//循环对象自身的属性
var item
//for in 对函数属性进行循环
for (item in f) {
    //高级浏览器已经存在 for in 中屏蔽了来自原型的属性
    //但是这里建议加上这个判断,保证程序的健壮性
    if(f.hasOwnProperty(item)) {
        console.log(item)
    }
}

原型链

JavaScript对象有一个指向一个原型对象的链。当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xbtUgEGA-1590071179192)(https://i.loli.net/2019/05/21/5ce3f4978a28565863.png)]

instanceof

instanceof运算符用于测试构造函数的prototype属性是否出现在对象的原型链中的任何位置

f instanceof Foo 的判断逻辑是:

f 的 __proto__一层层往上,能否对应到 Foo.prototype

在试着判断 f instanceof Object

作用域 闭包

面试题

1、说一下对变量提升的理解

  • 分为变量定义和函数声明(和函数表达式的区别)

  • 全局作用域和函数作用域或者是(ES6)中块级作用域,变量声明(var)和函数声明会被提升到作用域顶部,而函数表达式不会,只是当做一个var变量提升,函数没有被提升,另外ES6 中 let 和 const标识符定义的变量也不会发生提升(暂时性死区 TDZ)

2、说明this几种不同的使用场景

  • 作为构造函数执行
  • 作为对象属性执行
  • 作为普通函数执行
  • call apply bind
  • 箭头函数中(ES6)

this指的是当前运行的环境,即执行的时候

3、创建10个a标签,点击的时候弹出来对应的序号

var i;
for(i=0, i<10,i++) {
    var a = document.createElement('a');
    (function(i) {
        a.innerHTML= a +'<br>';
        a.addEventListener('click', function(e) {
            e.preventDefault();
            alert(i)
        })
        document.body.appendChild(a);
    })(i)
}

4、如何理解作用域

  • 自由变量: 在当前作用域没有被定义的变量
  • 作用域链:若在当前作用域找不到,则往父级作用域查找,自由变量的查找
  • 父级作用域:函数定义时所在的上下文
  • 闭包两个场景,作为参数传入和函数返回值

5、实际开发中闭包的应用

  • 闭包实际引用中主要用于封装变量,收敛权限

    function isFirstLoad() {
        var _list = []
        return function(id){
            if(_list.indexOf(id) >= 0){
                return false;
            }else {
                _list.push(id)
                return true
           }
        }
    }
    var firstload = isFirstLoad();
    firstload(10); //true
    firstload(10); //false
    firstload(20); //true
    

执行上下文

范围:一段

全局:变量定义、函数声明

函数:变量定义、函数声明、this、arguments、函数

  • 函数声明:function fnName () {…};
  • 函数表达式 var fnName = function () {…};
  • 匿名函数:function () {};
  • 匿名函数属于函数表达式

PS:注意“函数声明”和“函数表达式”的区别

  • JS解析时会提升当前作用域的函数声明,而函数表达式必须等到JS引擎执行到时,才会解析函数表达式。
  • 函数表达式后面可以加括号立即调用该函数,函数声明不可以
function fn(name) {
    //函数 开头默认创建this arguments
    console.log(this)
    console.log(arguments)
    
    age = 20 //声明提前
    console.log(name, age)
    var age
    
    bar(100)
    function bar(num) {
        console.log(num)
    }
}
  • 变量定义会把声明提前(undefined)未赋值,函数声明会把整个函数提前,函数表达式只会把函数名提前
console.log(a)//undefined
var a = 100

fn('zhangsan') //'zhangsan' 20
function fn(name) {
    age = 20
    console.log(name, age)
    var age
}

this

在全局环境中,this 永远指向 window。

  • this 要在执行时才能确认值,定义时无法确认

    var a = {
        name: 'A',
        fn: function() {
            console.log(this.name)
        }
    }
    a.fn() //this === 'a'
    a.fn.call({name: 'B'}) //this === {name: 'B'}
    var fn1 = a.fn
    fn1() //this === window
    
  • 作为构造函数执行

  • 作为对象属性执行

  • 作为普通函数执行 (this指向window)

  • call apply bind

function Foo(name) {
    this.name = name
}
//由一个函数new出来的对象,this就代表即将new出来的这个对象
var f = new Foo('zhangsan')

var obj = {
    name: 'A'
    printName: function() {
		console.log(this.name)//构造函数中的this
	}
}
obj.printName() //对象中的this指向该对象 this === 'A'

function fn() {
    console.log(this) //this === window 普通函数中的this永远指向window。 
}
fn()

//call apply bind
function fn1(name , age) {
    alert(name)
    console.log(this)
}
fn1.call({x:100}, 'zhangsan', 20) //常用
fn1.apply({x:100}, ['zhangsan', 20])
//bind 要用函数表达式
var fn2 = function (name, age) {
    alert(name)
    alert(age)
    console.log(this)
}.bind({y: 200}, 'lisi', 11);
fn2('zhangsan', 20)

作用域

  • JS没有块级作用域,不要块中定义变量
  • 只有函数和全局作用域

作用域链

自由变量不停的往父级作用域查找,就是所谓的作用域链

var a = 100
function F1() {
    var b = 200
    //当前作用域没有定义的变量,即“自由变量”
    function F2() {
        console.log(a) // a是自由变量
    	console.log(b) // b是自由变量
        console.log(c)
    }
    F2()
}

闭包

有权访问另一个函数作用域中的变量的函数

  • 函数作为返回值,在父作用域去找自由变量的值

  • 函数作为参数传递

 function F1() {
      var a = 100
      //返回一个函数 (函数作为返回值)
      return function() {
          console.log(a)
      }
  }
  //f1的得到一个函数
  var f1 = F1()
  var a = 200
  F1()// 打印 100
  
  function F2(fn) { //函数作为参数传递
     var a = 200
     fn()
  }
  F2(f1) // 打印 100

异步单线程

面试题目

  • 同步和异步的区别是什么?分别举一个同步和异步的例子
    • 同步会阻塞代码执行,而异步不会
    • alert是同步,setTimeout是异步
  • 一个关于setTimeout的笔试题
  • 前端使用异步的场景有哪些?
    • 定时任务:setTimeout setInverval
    • 网络请求:ajax 动态加载
    • 事件绑定

什么是异步(对比同步)

前端使用异步的场景

  • 在可能发生等待的情况,等待过程中不能相当alert一样阻塞程序运行,因此在发生等待的时候需要异步。
  • 定时任务:setTimeoutsetInverval
  • 网络请求:ajax请求,动态加载
  • 事件绑定
console.log('start')
//网络请求
$.get('./data1.json',function(data1) {
    console.log(data1)
})
//图片加载
var img= document.createElement('img')
img.onload = function() {
    console.log('loaded')
}
img.src = '/xxx.png'
//事件绑定
document.getElementById('btn1').addEventListener('click',function() {
  alert('clicked')  
})
console.log('end')

异步和单线程

其他知识

面试题

  • 获取 2017-06-10格式的日期

    function formatDate(dt) {
        if(!dt) {
            dt = new Date()
        }
        var year = dt.getFullYear()
        var month = dt.getMonth()
        var date = dt.getDate()
        if(month < 10){
            month = '0' + month
        }
        if(date < 10){
            date = '0' + date
        }
        return year + '-' + month + '-' + date
    }
    console.log(formatDate(new Date()))
    
  • 获取随机数,要求是长度一致的字符串格式

    var random = Math.random()
    random = random + '0000000000';
    random = random.slice(0, 10);
    console.log(random)
    
  • 写一个能遍历对象和数组的通用 forEach 函数

    function forEach(obj, fn){
        var key
        if(obj instanceof Array){
            obj.forEach(function(item, index) {
                fn(index, item)
            })
        }else {
            if(obj.hasOwnProperty(key)){
                for(key in obj) {
                    fn(key, obj[key])
                }
            }
        }
    } 
    var arr = [1,2,3]
    forEach(arr, function(index, item) {
        console.log(index, item)
    })
    var obj = {x:100, y:200}
    forEach(obj, function(key, val) {
        console.log(key, val)
    })
    

日期

Date.now() //获取当前毫秒数
var dt = new Date()
dt.getTime() //获取毫秒数
dt.getFullYear() //年
dt.getMonth() //月 (0 - 11) 要加上 +1
dt.getDate() //日 (0 - 31)
dt.getHours() //小时 (0 - 23)
dt.getMinutes() //分钟 (0 - 59)
dt.getSeconds() //秒 (0 - 59)

Math

Math.random() //获取随机数 在链接后加随机数,可以清除缓存

数组API

  • forEach 遍历所有元素
  • every 判断所有元素是否都符合条件
  • some 判断是否有至少一个元素符合条件
  • sort 排序
  • map 对元素重新组装,生成新数组
  • filter 过滤符合条件的元素
var arr = [1,2,3]
arr.forEach(function(item, index) {
	//遍历数组的所有元素
    console.log(index, item)
});

var result1 = arr.every(function(item, index) {
    //用来判断所有的数组元素,都满足一个条件
    if(item < 4){
        return true
    }
})
console.log(result1)

var result2 = arr.some(function(item, index) {
    //用来判断所有的数组元素,只要有一个满足条件即可
    if(item < 2) {
        return true
    }
})
console.log(result2)

var arr1 = [1,4,2,3,5]
var arr2 = arr1.sort(function(a, b) {
    // 从小到大排序
    //return a - b
    
    //从大到小排序
    return b - a
})
console.log(arr2)

var arr3 = [1,2,3,4]
var arr4 = arr.map(function(item, index) {
    //将元素重新组装,并返回
    return '<b>' + item +'</b>'
})
console.log(arr4)

var arr5 = arr.filter(function (item, index) {
    //通过某一个条件过滤数组
    if(item >= 2) {
        return true
    }
})
console.log(arr5)

对象API

  • for(key in obj) {}
var obj = {
    x: 100,
    y: 200,
    z: 300
}
var key
for(key in obj){
    //注意这里的 hasOwnProperty ,过滤掉原型链中的属性
    if(obj.hasOwnProperty(key)) {
        console.log(key, obj[key])
    }
}

JS-WEB-AIP

常说的JS(浏览器执行的JS)包含两部分

  • JS基础知识:ECMA 262标准
  • JS-Web-API:W3C 标准,
    • 没有规定任何JS基础相关的东西(变量类型、原型、作用域和异步。。。)
    • 只管定义用于浏览器中JS操作页面的API和全局变量

DOM操作

  • DOM是哪种基本的数据结构

  • DOM操作的常用API有哪些

    • 获取DOM节点,以及节点property和Attribute
    • 获取父节点,获取子节点
    • 新增节点,删除节点
  • DOM节点的Attribute 和property有和区别

    • property是DOM中的属性,是Javascript里的对象;
    • Attribute是HTML上的特性,它的值必须是字符串或者null
    • 自定义的Property与Attribute不同步,不相等
    • 非自定义的DOM property与 attributes 是有条件同步的
    • 在IE<9中,浏览器会把所有的property和attribute强制映射
    var elem = document.getElementById( 'id' );
    //Attribute 添加,删除,获取
    elem.getAttribute('gameid')
    elem.getAttribute('gameid') 
    elem.removeAttribute('gameid')
    
    //property 添加,删除,获取
    elem.gameid = 880; // 添加
    console.log( elem.gameid ) // 获取
    delete elem.gameid // 删除
    

DOM的本质

Element节点

  • 新增节点

    var div1 = document.getElementById('div1')
    //添加新节点
    var p1 = document.createElement('p')
    p1.innerHTML = 'this is p1'
    div1.appendChild(p1) //添加新创建的元素
    //移动已有节点
    p2 = document.getElementById('p2')
    div1.appendChild('p2')                                
    
  • 获取父元素

    var div1 = document.getElementById('div1')
    var parent = div1.parentElement;
    
    var child = div1.childNodes
    div1.removeChild(child[0])
    
  • 获取子元素

  • 删除节点

获取DOM节点

var div1 = document.getElementById('div1')//元素
var divList = document.getElementsByTagName('div') //集合
console.log(divList.length)
console.log(divList[0])

var containerList = document.getElementsByClassName('.container')//集合
var plist = document.querySelectorAll('p') //集合

节点属性property

  • style
  • className
  • nodeName
  • nodeType

Attribute属性操作

BOM

  • 如何检测浏览器的类型
  • 拆解url的各部分

navigator(浏览器)

  • navigator.userAgent (简称ua)

screen

  • screen.width
  • screen.height

location

  • location.href //地址
  • location.protocol //协议
  • location.host //域名
  • location.pathname //路径
  • location.search //查询字符串
  • location.hash //哈希

history

  • history.back() //返回
  • history.forward() //前进

事件绑定

题目

  • 编写一个通用的事件监听函数
var btn = document.getElementById('btn1')
btn.addEventListener('click', function(event) {
    console.log('clicked')
})
function bindEvent(elem, type, fn) {
    elem.addEventListener(type, fn)
}
var a = document.getElementById('link1')
bindEvent(a , 'click', function(e) {
    e.preventDefault()//阻止默认行为
    alert('clicked')
})
  • 描述事件冒泡流程
  • 对于一个无限下拉加载图片的页面,如何给每个图片绑定事件

DOM事件流

DOM事件流(event  flow )存在三个阶段:事件捕获阶段、处于目标阶段、事件冒泡阶段。 dom标准事件流的触发的先后顺序为:先捕获再冒泡

  • 事件捕获(event  capturing):当鼠标点击或者触发dom事件时,浏览器会从根节点开始由外到内进行事件传播,即点击了子元素,如果父元素通过事件捕获方式注册了对应的事件的话,会先触发父元素绑定的事件。
//addEventListener最后一个参数,为true则代表使用事件捕获模式,
//false则表示使用事件冒泡模式。即默认模式
window.addEventListener('click', function() {
  console.log('4. You click window');
}, true);
  • **事件冒泡(dubbed  bubbling):**与事件捕获恰恰相反,事件冒泡顺序是由内到外进行事件传播,从目标元素直到根节点。
  • 阻止事件冒泡 stopPropagation()

关于IE低版本的兼容性

  • IE低版本使用attachEvent绑定事件,和W3C标准不一样
  • IE低版本使用量已经非常少,很多网站都早已不支持
  • 建议对IE低版本的兼容性:了解即可,无需深究
  • 如果遇到对IE低版本要求苛刻的面试,果断放弃
  • IE10及以下不支持捕获型事件,所以就少了一个事件捕获阶段,IE11、Chrome 、Firefox、Safari等浏览器则同时存在。

通用事件绑定与解绑

代理

事件委托

JS开发环境

  • IDE(写代码的效率)

版本管理GIT

JS模块化

打包工具

运行环境

页面渲染

性能优化

JS面试题

6、window.onload 和 DOMConetentLoaded 的区别?

考点:浏览器渲染过程

7、用JS创建10个标签, 点击的时候弹出来对应的序号?

考点:作用域

8、简述如何实现一个模块加载器, 实现类似require.js的基本功能?

考点:JS模块化

9、实现数组的随机排序?

考点:JS基础算法

HTTP

同源策略

同源策略可防止 JavaScript 发起跨域请求。源被定义为 URI、主机名和端口号的组合。此策略可防止页面上的恶意脚本通过该页面的文档对象模型,访问另一个网页上的敏感数据。

跨域

原因:浏览器同源策略导致了跨域

作用:隔离恶意文件的重要安全机制

解决:

  1. jsonp,允许script加载第三方资源
  2. 反向代理(nginx服务器配置Access-Control-Allow-Origin)
  3. cors前后端协议头设置Access-Control-Allow-Origin
  4. iframe嵌套通讯,postmessage,浏览器插件

jsonp原理详解

猜你喜欢

转载自blog.csdn.net/qq_45832807/article/details/106269108