前端JS面试题
call 和 apply 的区别 是什么,哪一个性能更好一些?
- 传参的区别
- call 是一个个传递
- apply是传递一个数组
- bind 是预先处理,但是不会立即执行
- 但是 call 在基于ES6的展开运算符也可以将数组中的每一项传递给函数
- call apply 都是 Function 原型上面的方法,而每个函数作为 Function 的实例,可以调取其方法
- 在参数过多的时候,call 的性能会好一些
- console.time 可以测试一段程序执行的时间 console.timeEnd
实现 (5).add(3).minus(2) 使其输出结果为 6
假设 arr 是一个数组,它之所以可以调用 arr.push()这个方法式因为 它是Array的实例,可以继续调用Array原型上面的 push 方法
~function(){
//每一个方法执行完,都要返回Number这个类的实例,这样才可以继续调取Number类原型中的方法(链式写法)
function check(n){
n = Number(n)
return isNaN(n) ? 0 : n
}
function add(n){
n = check(n)
//this 不能赋值
return this + n
}
function minus(n){
n = check(n)
return this - n
}
['add','minus'].forEach(item=>{
Number.prototype[item] = eval(item)
})
}()
console.log((5).add(3).minus(2))
箭头函数与普通函数(function)的区别是什么?构造函数(function) 可以使用new 生成实例,那么箭头函数可以么?为什么
箭头函数与普通函数区别
-
箭头函数语法上比普通函数更加简洁(ES6中每一种函数都可以使用形参默认值和剩余运算符)
-
箭头函数中没有自己的this,他里面的this 从属于函数所处的上下文(使用call/apply等任何方式都无法改变this的指向)
document.body.onclick = function(){ //this:boby arr.sort(function(){ //this:window /***********回调函数中的this一般都是window***************/ return a -b }) } /*回调函数:把一个函数b作为实参传递给另外一个函数A ,函数A在执行的时候,可以把传递进来的函数B去执行*/
-
箭头函数没有arguments(类数组),只能基于…arg获取传递得参数集合(数组)
-
箭头函数不能被 new 执行(因为:箭头函数没有this 也没有prototype)
如何把一个字符串中得大小写取反(大写变小写,小写变大写)l
let str = 'sdsdsdSDSAD哈哈哈sdjsHJJkj';
str = str.replace(/[a-zA-Z]/g,content=>{
//俩种思路
//先将其转换然后在比较 如果相同代表之前也为大写
//2.根据AScLL值进行比较
return content.toUpperCase() ===content ? content.toLowerCase : content.toUpperCase()
})
实现一个字符串匹配算法,从字符串 S 中,查找是否存在字符串 T ,若存在返回所在位置,不存在返回 -1! (如果不能基于 indexof / includes等内置得方法,你会怎么处理)
//使用正则
~function(){
function myIndexof(T){
let reg = new RegExp(T),
res = reg.exec(this);
return res===null?res=-1:res.index
}
String.prototype.myIndexof = myIndexof
}()
//常规解法
~function(){
function myIndexof(T){
let lenT = T.length,
lenS = this.length,
res = -1;
if (lenT >lenS) return res;
for (var i = 0; i <= lenS - lenT; i++) {
if(this.substr(i,lenT)=== T){
res = i
break;
}
}
return res
}
String.prototype.myIndexof = myIndexof
}()
输出下面代码运行结果
var a = {}, b = '123',c =123;
a[b] = 'b'
a[c] = 'c'
console.log(a[b])
a = {100 : '哈哈','100' :'嘻嘻'} //输出{100: "嘻嘻"}
//因为后面得会替换前面得 对象属性key 可以为字符串也可以数字 a['123'] <=>a[123]
var a = {}, b = Symbol('123'),c =Symbol(123);
a[b] = 'b'
a[c] = 'c'
console.log(a[b])
//Symbol 是ES6中新增得数据类型, Symbol(123) === Symbol(123) false 它创建出来得值是唯一值
var a = {}, b = {key:'123'},c ={key:'456'};
a[b] = 'b'
a[c] = 'c'
console.log(a[b])
//({key:'123'}).toString()==>>>"[object Object]"\
//obj ={} arr = [12,23] obj[arr] = 'xzt' obj => {'12,23':'xzt'}
在输入框中如何判断输入的是一个正确的网址,例如:用户输入一个字符串,验证是否符合URL网址的格式
let str = 'http://www.baidu.com/index.html?lx=1&from=wx#video'
let reg = /^((http|https|ftp):\/\/)?(([\w-]+\.)+[a-z0-9]+)((\/[^/]*)+)?(\?[^#]+)?(#.+)?$/i
console.log(reg.exec(str))
判断输出题
function Foo(){
Foo.a = function(){
console.log(1)
}
this.a = function(){
console.log(2)
}
}
//把Foo当做类,在原型上设置实例共有的属性方法=》实例.a()
Foo.prototype.a = function(){//原型上面加属性和方法
console.log(3)
}
//把Foo当做普通对象设置私有的属性方法 =》Foo.a()
Foo.a = function(){//在Foo上加了私有属性
console.log(4)
}
Foo.a()//输出4
let obj = new Foo() //生成 Foo 实例 obj 可以调取原型上的方法 Foo.a : f=>1 obj.a:f=>2
obj.a()//输出2 私有属性中有a
Foo.a()//在私有属性上面找 所以输出1
编写代码实现图片懒加载
- 前端性能优化的重要方案
- 通过图片或者数据的延迟加载,我们可以加快页面渲染的速度,让第一次打开页面的速度变快
- 只有滑动到某个区域,我们才加载真是的图片,这样也可以节省加载的流量
- 处理方案
- 把所有需要延迟加载的图片用一个盒子包起来,设置宽搞和默认站位图
- 开始让所有的img src 为空,把真实图片的地址放到IMG的自定义属性上,让IMG隐藏
- 等到所有其他资源都加载完成后,我们再开始加载图片
- 对于很多图片,需要当页面滚动的时候,当前图片区域完全显示出来后再加载真实图片
实现一个$attr(name,value)遍历,属性为 name 值为 value 的元素集合
function $attr(props,value){
let el = document.getElementsByTagName('*'),
arr = [];
// [].forEach.call(el,item=>{})
el = Array.from(el)//把非数组转换成数组
el.forEach(item=>{
//存储的是当前元素props 对应的属性值
let itemVale = item.getAttribute(prop)
if (prop === 'class') {
//样式类属性名要特殊处理
new RegExp("\\b" + value + "\\b").test(itemVale)?arr.push(item):null;
return;
}
if (itemVale===value) {
//获取的值 和传递的值校验成功:当前就是我们想要的
arr.push(item)
}
})
return arr
}
英文字母汉字组成的字符串,用正则给英文单词前后加空格
let reg = \\b[a-z]+\b\ig;
str = str.replace(reg,value=>{
return " " + value + " "
}).trim()//首尾去除空格
编写一个程序,将数组扁平化,并去除其中重复部分数据,最终得到一个升序而且不重复的数组
自己实现new
function myNew(Fn,...arg){
//创建一个空对象
// let obj = {}
// //让他的原型链之指向 Fn.prototype(作为Fn 的一个实例)
// obj.__proto__ = Fn.prototype
let obj = Object.create(Fn.prototype)
//Object.create(AA对象):创建一个空对象,并且让空对象obj作为AA对象所属构造函数的实例
//(obj.__proto__ = AA)
Fn.call(obj,...arg)
return obj
}
合并数组
let ary1 = ['A1','A2','B2','C1','C2','C3','D','D1']
let ary2 = [A','B2','C1]
let n= 0
for(var i = 0;i<ary2.length;i++){
let item2 = ary2[i]
for(var j=0;j<ary1.length;j++){
let item1 = ary2[j]
if(item1.includes(item2)){
n = j
}
}
ary1.splice(n+1,0,item2)
}
判断输出值
var b = 10;
(function b(){
b= 20;
console.log(b) //输出 b 这个函数
})()
console.log(b)//输出10
let fn = function AAA(){
AAA = 100
console.log(AAA)//当前函数
}
AAA()==》 报错
1、本应匿名的函数如果设置了函数名,在外面还是无法调用,但是在函数里面是可以使用的
2、而且类似于创建常亮一样,这个名字存储的值不能再被修改(非严格模式下不报错,但是不会有任何效果,严格模式下直接报错,我们可以把AAA理解为是用const 创建出来的)
赋值比较
-
== 进行比较的时候,如果俩边数据类型不一样,则先转换为相同的数据类型,然后在进行比较
- {} == {} 俩个对象进行比较,比较的是堆内存的地址
- null == undefined 相等 的 / null === undefined 不相等
- NaN == NaN 不相等,NaN 和谁都不相等
- [12] == ‘12’ 对象和字符串比较,是把对象 toString()转换为字符串后再进行比较的
- 剩余所有情况在进行比较的时候,都是转换为数字(前提数据类型不一样)
- 对象转数字:先转换为字符串,然后在转换为数字
- 字符串转数字:只要出现一个非数字字符,结果就是NaN
- 布尔转数字 :true => 1 false =>0
- null 转数字 0
- undefined 转数字 NaN
-
实现 a1 && a2 && a==3
if (a==1 && a==2 && a==3){ console.log('OK') } //方法一: var a = { n = 0; toString :function (){ return ++this.n; } } //方法二: //shift :删除第一项,把删除的内容返回,原有数组改变 let a = [1,2,3] a.toString = a.shift //方法三: letn = 0 Object.definProperty(window,'a',{ get :function(){ return ++n } })
判断输出
let obj = {
2:3,
3:4,
length:2,
push : Array.prototype.push
}
obj.push(1)
//this : obj obj[obj.length] = 1 ==> obj[2] = 1 ==> obj.length = 3
obj.push(2)
//this : obj obj[obj.length] = 2 ==> obj[3] = 1 ==> obj.length = 4
console.log(obj)
Array.prototype.push = function AA(){
this[this.length] = val
//=>this.length 在原来的基础上加一
return this.length
}
冒泡排序
let ary = [1,5,4,2,8,9]
let ary = [1,5,4,2,8,9]
function Bubble(ary){
for (var i = 0; i < ary.length-1; i++) {
for (var j = 0; j < ary.length-i-1; j++) {
let temp;
if (ary[j] > ary[j+1]) {
temp = ary[j]
ary[j] = ary[j+1]
ary[j+1] =temp
}
}
}
return ary
}
插入排序
function insert(ary){
let handel = []
handel.push(ary[0])
for (var i = 1; i < ary.length; i++) {
let itemA = ary[i]
for (var j=handel.length-1;j>=0; j--) {
let itemB = handel[j]
if (itemA>itemB) {
handel.splice(j+1,0,itemA)
break;
}
if (j===0) {
handel.unshift(itemA)
}
}
}
return handel
}
函数柯理化:预先处理的思想(利用闭包的机制)
请实现一个add 函数,满足以下功能
add(1)
add(1)(2)
add(1)(2)(3)
add(1)(2)(3)(4)