总结前端基础面试题范围
- JS基础(原型、原型链、作用域、闭包、异步、单线程)
- JS-WEB-API(DOM操作、Ajax、事件绑定)
- JS-开发环境(版本管理、模板化、打包工具、页面渲染、性能优化)
列举几个面试题
- JS中使用typeof能得到的哪些类型?(考点:JS变量类型)
- 何时使用===何时使用==?(考点:强制类型转换)
- window.onload和DOMContentLoader的区别?(考点:浏览器渲染过程)
- 用JS创建10个标签,点击的时候弹出来对应的序号?(考点:作用域)
JS基础
JS中使用typeof能得到的哪些类型?
7种:number、string、undefined、boolean、symbol、object、function
typeof 1 // 'number'
typeof '1' // 'string'
typeof undefined // 'undefined'
typeof true // 'boolean'
typeof Symbol() // 'symbol'
typeof b // b 没有声明,但是还会显示 undefined
typeof [] // 'object'
typeof {} // 'object'
typeof console.log // 'function'
typeof null // 'object'
复制代码
何时使用===何时使用==?
if (obj.a == null) {
// 这里相当于obj.a === null || obj.a === undefined, 简写形式
// jquery源码中推荐的写法
}
=== 全等:值和类型都相等
== 等:值相等 null == undefined
复制代码
JS中的内置函数
Object、Array、Boolean、Number、String、Function、Date、RegExp、Error
复制代码
JS按存储方式区分变量类型
基本类型有6种,分别是undefined,null,boolean,string,number,symbol(ES6新增)。
虽然 typeof null 返回的值是 object,但是null不是对象,而是基本数据类型的一种。
基本数据类型存储在栈内存,存储的是值。
复杂数据类型的值存储在堆内存,地址(指向堆中的值)存储在栈内存。当我们把对象赋值给另外一个变量的时候,复制的是地址,指向同一块内存空间,当其中一个对象改变时,另一个对象也会变化。
// 值类型
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
在条件判断时,除了 undefined, null, false, NaN, '', 0, -0,其他所有值都转为 true,包括所有对象。
复制代码
变量类型和计算
1.变量计算-强制类型转换
(1).字符串拼接
var a=100+10; // 110
var b=100+'10'; // '10010'
(2).==运算符
100 == '100' // true
0 == '' // true
null == undefined // true
(3).if语句
1.var a=true;
if(a){
//....
}
2.var b=100;
if(b){
//...
}
3.var c= '';
if(c){
//..
}
(4).逻辑运算
console.log(10 && 0); // 0
console.log('' || 'abc'); // 'abc'
console.log(!window.abc); // true
复制代码
如何理解JSON
JSON只不过是一个JS对象而已
JSON.stringify({a:10, b:20});
JSON.parse('{"a":10, "b": 20}')
复制代码
原型构造函数
// 构造函数
function Foo(name, age) {
this.name = name;
this.age = age;
this.class = "class-1";
//return this 默认有这一行
}
var f = new Foo("cl", 29); // 创建多个对象
// 构造函数-扩展
1.var a={}其实是var a=new Object()的语法糖。
2.var a=[]其实是var a=new Array()的语法糖。
3.function Foo(){....}其实是var Foo=new Fuction(...)
4.使用instanceof判断一个函数是否是一个变量的构造函数。
// 原型规则和示例
1.所有的引用类型(数组、对象、函数 ),都具有对象特性,即可自由扩展属性(除了"null"之外)。
var obj={};obj.a=100;
var arr=[];arr.a=100;
function fn(){} fn.a=100;
2.所有的引用类型(数组、对象、函数 ),都有一个__proto__属性,属性值是一个普通的对象。
console.log(obj.__proto__);
console.log(arr.__proto__);
console.log(fn.__proto__);
3.所有的函数,都有一个prototype属性,属性值也是一个普通的对象。
console.log(fn.prototype)
4.所有的引用类型(数组、对象、函数 ),__proto__属性值指向它的构造函数的"prototype"属性值。
console.log(obj.__proto__===Object.prototype);
5.当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么会去它的__proto__(即它的构造函数的prototype)中寻找。
//构造函数
function Foo(name, age){
this.name=name;
}
Foo.prototype.alertName = function(){
alert(this.name);
}
//创建示例
var f=new Foo('小林');
f.printName=function(){
console.log(this.name);
}
//测试
f.printName();
f.alertName();
f.toString();//要去f.__proto__.__proto__中查找。
复制代码
原型链
instanceof
1.f instanceof Foo的判断逻辑是:
2.f的__proto__一层一层往上,能否对应到Foo.prototype。
3.再试着判断f instanceof Object。
复制代码
如何准确判断一个变量是数组类型
var arr = [];
arr instanceof Array // true
typeof arr // object、typeof是无法判断是否是数组的
复制代码
写一个原型链继承的列子
function Animal() {
this.eat = function() {
console.log("animal eat");
}
}
function Dog() {
this.back = function() {
console.log("dog back")
}
}
Dog.prototype = new Animal();
var hashiqi = new Dog();
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);
}
var div1 = new Elem('box');
// console.log(div1.html());
// div1.html('<p>2222</p>');
// div1.on("click", function(){
// alert("111");
// })
div1.html('<p>2222</p>').on("click", function(){
alert("this.text()");
})
复制代码
描述new一个对象的过程
1.新生成了一个对象
2.链接到原型
3.绑定 this
4.返回新对象
function create() {
// 创建一个空的对象
let obj = new Object()
// 获得构造函数
let Con = [].shift.call(arguments)
// 链接到原型
obj.__proto__ = Con.prototype
// 绑定 this,执行构造函数
let result = Con.apply(obj, arguments)
// 确保 new 出来的是个对象
return typeof result === 'object' ? result : obj
}
复制代码
this指向
(1).this要执行时才能确认值,定义时无法确认
1.作为构造函数执行
function Foo(name){
this.name=name;
}
var f=new Foo("小林")
2.作为对象属性执行
var obj = {
name:"A",
printName: function(){
console.log(this.name);
}
}
obj.printName()
3.作为普通函数执行
function fn(){
console.log(this)//window
}
fn()
4.call、Apple、bind
function fn1(name, age){
alert(name);
console.log(this)//window
}
fn1.call({x:100}, "zhangsan", 20);//{x:100}this指向
fn1.call({x:100}, ["zhangsan", 20]);//{x:100}this指向
var fn2 = function(name, age){
alert(name);
console.log(this);
}.bind({y:200})
fn2('zhangsan', 20)
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
复制代码
作用域
(1).没有块级作用域。
if(true){
var name="zhangsan";
}
console.log(name);
(2).只有函数和全局作用域。
var a=100;
function fn(){
var a=200;
console.log('fn', a);
}
console.log('global', a);
fn();
(3).作用域链
var a=100;
function fn(){
var b=200;
console.log(a)//当前作用域没有定义的变量,即“自由变量”
console.log(b)
}
复制代码
执行上下文
(1).范围:一段<script>或者一个函数。
(2).全局:变量定义、函数声明。一段<script>。
(3).函数:变量定义、函数声明、this、argument、函数
(4).函数声明和函数表达式的区别。
console.log(a); //undefined
var a = 100;
fn('zhangsan') // 'zhangsan' 20
function fn(name) {
age=20;
console.log(name,age);
var age;
}
复制代码
闭包
1.函数作为返回值。
function f1(){
var a=10;
return function (){
console.log(a)//自由变量,父作用域寻找
}
}
var f =f1();
var a=2000;
f();// 10
2.函数作为函数传递。
function f1(){
var a=10;
return function (){
console.log(a)//自由变量,父作用域寻找
}
}
var f =f1();
function f2(fn){
var a=200;
fn();
}
f2(f1);
复制代码
说一下对变量提升的理解
详解: www.cnblogs.com/liuhe688/p/…
下面这个ul,请写出代码,让点击每一列的时候alert其index?
<ul id="test">
<li>这是第一条</li>
<li>这是第二条</li>
<li>这是第三条</li>
<li>这是第四条</li>
<li>这是第五条</li>
</ul>
<script>
//第一种方式,加入index属性
// window.onload = function() {
// var test = document.getElementById('test');
// var list = test.getElementsByTagName('li');
// for(var i=0; i<list.length; i++){
// list[i].setAttribute('index',i);
// list[i].addEventListener("click", function(){
// console.log(this.getAttribute('index'), 'index');
// })
// }
// };
// 第二种方式,用let申明变量
// window.onload = function() {
// const test = document.getElementById('test');
// const list = test.getElementsByTagName('li');
// for(let i=0; i<list.length; i++){
// list[i].addEventListener("click", function(){
// console.log(i, 'let');
// })
// }
// };
//方法3,使用闭包的方式
// window.onload = function() {
// var test = document.getElementById('test');
// var list = test.getElementsByTagName('li');
// for(var i=0; i<list.length; i++) {
// list[i].addEventListener("click", function(num) {
// return function() {
// console.log(num);
// }
// }(i))
// }
// }
// 方法4
// window.onload = function() {
// var test = document.getElementById('test');
// var list = test.getElementsByTagName('li');
// for(var i=0; i<list.length; i++){
// list[i].index=i
// list[i].addEventListener("click", function(e){
// console.log(e.target.index);
// })
// }
// };
</script>
复制代码
如何理解作用域
详解: www.cnblogs.com/smyhvae/p/8…
异步和单线程
1.何时需要异步
(1).在可能发生等待的情况。
(2).等待过程中不能像alert一样阻塞程序运行。
(3).因此,所以的'等待的情况'都需要异步。
2.异步的场景
(1).定时任务:setTimeout,setInverval。
(2).网络请求:ajax请求,动态<img>加载。
(3).事件绑定
3.同步和异步的区别是什么
(1).同步会阻塞代码执行,而异步不会。
(2).alert是同步,setTimeout是异步。
console.log(100);
setTimeout(function() {
console.log(200);
})
console.log(300);
1.执行第一行,打印100;
2.执行setTimeout后,传入setTimeout的函数会被暂存起来,不会立即执行(单线程的特点,不能同时干两件事);
3.执行最后一行,打印300;
4.待所有程序执行完,处于空闲状态时,会立马看有木有暂存起来的要执行;
5.发现暂存起来的setTimeout中的函数无需等待时间,就立即过来执行
复制代码
获取当天格式的日期
function formatDate(dt) {
dt = !dt ? new Date() : dt;
let year = dt.getFullYear();
let month = dt.getMonth() + 1;
let data = dt.getDate();
month = month < 10 ? ('0' + month) : month;
data = data < 10 ? ('0' + data) : data;
//强制转换
return year + "-" + month + "-" + data;
}
let dt = new Date();
console.log(formatDate(dt));
复制代码
获取随机数,要求是长度一直的字符串格式
var num = Math.random();
num = num + '0000000000';
num = num.slice(0, 10);
console.log(num);
复制代码
写一个能遍历对象和数组的forEach函数
function forEach(obj, fn) {
if (obj instanceof Array) {
// 准确判断是不是数组
obj.forEach(function(item, index){
fn(index, item);
})
} else {
// 不是数组就是对象
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
fn(key, obj[key]);
}
}
}
}
// 数组
const arr = [1,2,3];
forEach(arr, function(index, item) {
console.log(index, item);
})
//对象
const obj = { x:100, y: 200};
forEach(obj, function(key, value){
console.log(key, value);
})
复制代码
数组API
1.forEach
var arr=[1,2,3];
arr.forEach(function(item, index){
//遍历数组的所有元素
console.log(index, item);
})
2.every
var arr=[1,2,3];
var result=arr.every(function (item, index){
//用来判断所有的数组元素,都满足一个条件
if(item < 4){
return ture;
}
})
console.log(result);
3.some
var arr=[1,2,3];
var result = arr.some(function (item, index){
//用来判断所有的数组元素,只要有一个满足条件即可
if(item < 2){
return ture
}
})
console.log(result);
4.sort
var arr=[1,4,2,3,5];
var arr2 = arr.sort(function(a, b){
//从小到大排序
return a-b;
//从大到小排序
// return b-a
})
console.log(arr2);
5.map
var arr=[1,2,3,4];
var arr2 = arr.map(function(item, index){
//将元素重新组装,并返回
return '<b>'+item+'</b>';
})
console.log(arr2);
6.filter
var arr=[1,2,3];
var arr2=arr.filter(function(item, index){
//通过某一个条件过滤数组
if(item >= 2){
return true;
}
})
console.log(arr2);
复制代码
JS-WEB-API
DOM节点操作
DOM可以理解为:浏览器把拿到的html代码,结构化一个浏览器能识别并且js可操作的一个模型而已。
<div id="box">
<p class="container"></p>
<p></p>
<p></p>
</div>
<script>
var box = document.getElementById("box"); // 元素
var boxList = document.getElementsByTagName("p"); // 集合
console.log(boxList.length);
console.log(boxList[0]);
var box = document.querySelector("#box");
console.log(box);
var container = document.getElementsByClassName(".container");
console.log(container);
// property
var box = document.querySelectorAll("p"); //集合
console.log(box);
var p = box[0];
console.log(p.style.width); // 获取样式
p.style.width = "100px"; // 修改样式
console.log(p.className); // 获取class
p.className = "p1"; // 修改class
// 获取nodeName 和 nodeType
console.log(p.nodeName);
console.log(p.nodeType);
// Attribute
var box = document.querySelectorAll("p"); //集合
var p = box[0];
p.getAttribute("data-name");
p.setAttribute("data-name", "imooc");
p.getAttribute("style");
p.setAttribute("style","font-size: 30px");
// 新增节点
var box = document.getElementById("box");
// 添加新节点
var p = document.createElement("p");
p.innerHTML = "this is p";
box.appendChild(p); // 添加新创建的元素
// 移动已有节点
var p1 = document.getElementById("p1");
box.appendChild(p1);
// 获取父元素和子元素
var box = document.getElementById("box");
var parent = box.parentElement;
var child = box.childNodes;
box.removeChild(child[0]);
// 删除节点
var box =document.getElementById("box");
var child = box.childNodes;
box.removeChild(child[0]);
</script>
复制代码
DOM节点的Attribute和property有何区别
1.property只是一个JS对象的属性的修改
2.Attribute是对html标签属性的修改
复制代码
如何检测浏览器的类型
var ua = navigator.userAgent;
var isChrom = ua.indexOf("Chrome");
console.log(isChrom);
console.log(location.href); // 网址
console.log(location.protocol); //协议https
console.log(location.pathname); // 域名/p/4d6b43fda11a
console.log(location.search);
console.log(location.hash);
history.back();// 返回上页面,页面不刷新
history.forward(); //加载 history 列表中的下一个 URL。
复制代码
通用绑定事件的函数
<div id="box">
<a >2</a>
<a >3</a>
<a >4</a>
</div>
<script>
function bindEvent(elem, type, selector, fn) {
if (fn == null) {
fn = selector;
selector = null;
}
elem.addEventListener(type, function (e) {
let target;
if (selector) {
target = e.target;
if (target.matches(selector)) {
fn.call(target, e);
} else {
fn(e);
}
}
})
}
let box = document.getElementById("box");
bindEvent(box, 'click', 'a', function(e){
console.log(this.innerHTML);
})
<script>
复制代码
代理的好处
1.代码简洁
2.减少浏览器内存占用
复制代码