js常见面试题

1, BFC是什么?触发BFC的条件是什么?触发后又能解决什么问题呢

  • BFC是什么?
    形成一个完全独立的空间,让空间里的子元素不会影响到外面的布局。
    块格式化上下文是web页面可视化css渲染的一部分,是块盒子的布局过程发生的区域,也是浮动元素与其他元素交互的区域
  • 如何触发 BFC
    1,float不为none
    2,position不为relative和static
    3,overflow为auto scroll和hidden
    4,display值table-cell或 inline-block
  • 触发之后又能解决什么问题呢
    1,解决浮动元素令父元素高度坍塌的问题
    这是因为浮动的子元素脱离了文档流,可以给父级添加overflow: hidden;或者display: table-cell或者display: flex;或display: inline-block;或者position: fixed或者position: absolute
    如果处于布局需要可能无法给父元素设置这些属性,常用的还有其他办法:
  • 给父元素也加float: left
  • 给父元素增加固定高度
  • 在浮动的子元素后面增加一个空元素,并设置{clear: both}来清除浮动
  • 为浮动的最后一个子元素设置伪元素,::after{clear: both}
    2,两栏自适应布局
    比如左侧固定,右侧自适应
    方法1:
    左边左浮动,右边设置margin-left: 200px
    方法2:
    父元素position: relative;
    左边position: absolute;top: 0;left: 0;bottom: 0; width: 200px;
    右边margin-left: 200px;
    方法3:
    左边position: absolute;top: 0;left: 0;bottom: 0; width: 200px;
    右边左边position: absolute;top: 0;right 0;left: 200px;width: 100%;
    3,外边距垂直方向重合


    image.png

    方法1:
    给其中一个p元素添加父元素,并给父元素添加overflow: hidden;
    方法2:
    用padding代替margin
    2, 以下代码输出什么

var foo={n:1};
(function(foo){
   console.log(foo.n);
   foo.n=3;
   var foo={n:2};
   console.log(foo.n);
})(foo);
console.log(foo.n);
var foo = {n:1};
(function(foo){            //形参foo同实参foo一样指向同一片内存空间,这个空间里的n的值为1
    var foo;               //重复声明,无效。
    console.log(foo.n);    //输出1
    foo.n = 3;             //形参与实参foo指向的内存空间里的n的值被改为3
    foo = {n:2};           //形参foo指向了新的内存空间,里面n的值为2.
    console.log(foo.n);    //输出新的内存空间的n的值
})(foo);
console.log(foo.n);        //实参foo的指向还是原来的内存空间,里面的n的值为3.

3,cookies , sessionStorage 和 localStorage 的区别
4,JS中的原型和原型链
创建的每一个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途就是包含可以由特定类型的所有实例共享的属性和方法,简单点说就是,当我们创建函数的时候系统会自动分配一个prototype属性,用来存储可以让所有实例共享的属性和方法

每一个构造函数都有一个prototype属性,这个属性指向一个对象,也就是原型对象
原型对象默认拥有一个constructor属性,指向指向他的那个构造函数
每个对象都拥有一个隐藏的__proto__,指向他的原型对象
function Person() {}
var p = new Person()
p.__proto__ = Person.prototype // true
Person.prototype.constructor = Person  // true

原型特点:

function Person () {
    }
    Person.prototype.name = '小马'
    Person.prototype.age = 20
    Person.prototype.sayHi = function () {
        console.log('Hi')
    }
    var p1 = new Person()
    var p2 = new Person()
    p1.name = '小王'
    p1.age = 18
    console.log(p1.name)  // '小王'
    console.log(p1.age)   // 18
    console.log(p1.sayHi())   // Hi
    console.log(p2.name)  // '小马'
    console.log(p2.age)   // 20
    console.log(p2.sayHi())   // Hi

从代码中可以看出:

  • 实例可以共享原型上的属性和方法
  • 实例自身的属性和方法会屏幕原型上的属性和方法,实例上没有的会去原型上找
    以上代码可以写为:
function Person () {
    }
    Person.prototype = {
        name: '小马',
        age: 20,
        sayHi () {
            console.log('Hi')
        }
    }
    var p1 = new Person()
    var p2 = new Person()
    p1.name = '小王'
    p1.age = 18
    console.log(p1.name)  // '小王'
    console.log(p1.age)   // 18
    console.log(p1.sayHi())   // Hi
    console.log(p2.name)  // '小马'
    console.log(p2.age)   // 20
    console.log(p2.sayHi())   // Hi

只是在我们重写原型链的时候需要注意以下问题 :

function Person () {}
    var p = new Person ()
    Person.prototype = {
        name: '小王',
        age: 20
    }
    console.log(Person.prototype.constructor == Person)   // false
    console.log(p.name)  // undefined

重写原型对象之前:


image.png

重写原型对象之后:


image.png
  • 在已经创建了实例的情况下重写原型,会切断现有实例与新原型之间的联系
  • 重写原型对象,会导致原型对象的constructor指向Object,导致原型链关系混乱
    请看以下代码:
var arr = [1,2,4]
console.dir(arr.valueOf()) 
image.png
arr.__proto__ === Array.prototype
true
Array.prototype.__proto__ === Object.prototype
true
arr.__proto__.__proto__ === Object.prototype
true

// 原型链的终点
Object.prototype.__proto__ === null
true

原型链如下:

arr ---> Array.prototype ---> Object.prototype ---> null

这就是传说中的原型链,层层向上查找,最后还没有就返回undefined
4,JavaScript 中的继承
什么是继承?

继承是指一个对象直接使用另外一个对象的属性和方法
由此可见只要实现属性和方法的继承,就达到继承的效果
我们先创建一个Person类

function Person (name, age) {
    this.name = name
    this.age = age
}
// 方法定义在构造函数的原型上
Person.prototype.getName = function () { console.log(this.name)}

此时我想创建一个Teacher类,我希望它可以继承Person所有的属性,并且额外添加属于自己特定的属性

function Teacher (name, age, subject) {
    Person.call(this, name, age)
    this.subject = subject
}

属性的继承是通过在一个类内执行另外一个类的构造函数,通过call指定this为当前执行环境,这样就可以得到另外一个类的所有属性

function Person (name, age) {
        this.name = name
        this.age = age
    }
    Person.prototype.getName = function () {
        console.log(this.name)
    }
    function Teacher (name,age,sub) {
        Person.call(this,name, age)
        this.sub = sub
    }
    var te = new Teacher('小王', 18, '前端')
    console.log(te.age)   // 18
    console.log(te.name)   // '小王'

很明显Teacher成功继承了Person的属性
4, html5 本地存储( localStorage )相关 api ,并实现 getAll 方法,获取本地存储

  • setItem(key,value) 添加数据
  • getItem(key) 根据key获取值
  • key(index) 根据索引获取key
  • removeItem(key) 根据key删除一条数据
  • clear() 清空数据
  • length:获取总数据的长度
    getAll()这个方法我没太明白想获取什么,所以我采用了key()和getItem()方法结合获取键值对


    image.png

    5, JS中substr与substring的区别
    substr和substring都是截取字符串中子串,非常相近,可以有一个或两个参数
    语法:substr(start [,length]) 第一个字符的索引是0,start必选 length可选
    substring(start [, end]) 第一个字符的索引是0,start必选 end可选
    相同点:当有一个参数时,两者的功能是一样的,返回从start指定的位置直到字符串结束的子串

var str = "hello Tony";
str.substr(6);  //Tony
str.substring(6); //Tony

不同点:有两个参数时
(1)substr(start,length) 返回从start位置开始length长度的子串

“goodboy”.substr(1,6);   //oodboy

【注】当length为0或者负数,返回空字符串
(2)substring(start,end) 返回从start位置开始到end位置的子串(「不包含end」)

“goodboy”.substring(1,6);  //oodbo

【注】:
(1)substring 方法使用 start 和 end 两者中的较小值作为子字符串的起始点
(2)start 或 end 为 NaN 或者负数,那么将其替换为0
str是字符串时str.substring(start,end)和str.slice(start,end)完全等价;str是数组时str.slice(start,end)还可以继续用,str.substring(start,end)就不行了。
6, JS 截取地址栏指定字符后的内容
1,获取地址栏路径

var url = window.location.href;

2,截取指定字符后的内容

/**
  * 截取指定字符后的内容
  * @param url 路径
  * @param parameter 字符
  */
function getCaption(url,parameter) {
    var index = url.lastIndexOf(parameter);
    url = url.substring(index + 1, url.length);
    return url;
}

3,调用方法及结果

var url="http://www.baidu.com?123";
var a = getCaption(url, "?");
//  123

7, 以下代码的运行结果是

var game='4399'
game.substring(2,1)
console.log(game)
// 4399

这里有两个坑,第一个是start 8,JS的重定向有哪些

location.assign("http://www.baidu.com");
location.href="http://www.baidu.com";
window.location="http://www.baidu.com";
top.location="http://www.baidu.com";
self.location="http://www.baidu.com";
window.location.href="http://www.baidu.com";

9, 谈谈你对虚拟DOM原理的理解
1,什么是Virtual DOM(虚拟DOM)
Virtual DOM是对DOM的抽象,本质上是JavaScript对象,这个对象就是更加轻量级的对DOM的描述.
2,为什么需要Virtual DOM
既然我们已经有了DOM,为什么还需要额外加一层抽象?

首先,我们都知道在前端性能优化的一个秘诀就是尽可能少地操作DOM,不仅仅是DOM相对较慢,更因为频繁变动DOM会造成浏览器的回流或者重回,这些都是性能的杀手,因此我们需要这一层抽象,在patch过程中尽可能地一次性将差异更新到DOM中,这样保证了DOM不会出现性能很差的情况.

其次,现代前端框架的一个基本要求就是无须手动操作DOM,一方面是因为手动操作DOM无法保证程序性能,多人协作的项目中如果review不严格,可能会有开发者写出性能较低的代码,另一方面更重要的是省略手动DOM操作可以大大提高开发效率.

最后,也是Virtual DOM最初的目的,就是更好的跨平台,比如Node.js就没有DOM,如果想实现SSR(服务端渲染),那么一个方式就是借助Virtual DOM,因为Virtual DOM本身是JavaScript对象
10, 怎么利用img进行xss攻击
这个问题,我有点蒙蔽,我以为是 转发图片给上当者,然后就攻击了,网上找了答案,果不其然,我想错了
因为最近在学习web安全,出于好奇,尝试对CSDN进行了XSS注入,没想到真的成功了。
操作步骤:
直接找一篇博客,在底下评论

pic

因为CSDN做了简单的转义,他会将注入的标签,去掉闭合性,也就是会把我注入的内容处理为:

pic

这样,注入的标签就失去了他的闭合性了,对一些普通的攻击就进行了防御,但是onerror事件是专门针对js出错的,所以,标签闭合性被破坏刚好触发了这个事件,所以,他会被执行,执行之后将img标签的src属性替换成我们想要的属性,然而我注入的这个地址,故意又是一个不能访问的地址,于是,就反复的触发这个onerror事件,最终导致浏览器堆栈溢出了。
说明:如果图片存在,但网络很不通畅,也可能触发 onerror。
解决方法:

  • 1、用html转义,将<>转义成转义符,这样标签就编程了文本了(QQ空间的做法) - 2、使用jsoup白名单过滤掉onerror关键字,让他不要在前台显示(这种更安全,因为转义还有可能被绕过)
    10, split() join() 的区别
    前者是将字符串切割成数组的形式,后者是将数组转换成字符串
    11, 闭包
    。。。。
    12,display 有哪些值及其区别


    image.png

    13,position 有哪些值及其区别


    image.png

    14, px、em、rem 区别
    image.png

    15,CSRF攻击和防御
    防御:
    1,尽量使用post请求
    2,加入验证码
    3,验证Referer

    4.Token
    5, 加入自定义header
    16, XSS原理和防御
    XSS 攻击,即跨站脚本攻击(Cross Site Scripting),它是 web 程序中常见的漏洞。
    原理
    攻击者往 web 页面里插入恶意的 HTML 代码(Javascript、css、html 标签等),当用户浏览该页面时,嵌入其中的 HTML 代码会被执行,从而达到恶意攻击用户的目的。如盗取用户 cookie 执行一系列操作,破坏页面结构、重定向到其他网站等。
    17,for in for of区别

1,for in 取的是key  for of 取的是value
2, for in 可以用于数组和对象 for of只能用于数组遍历

charAt() 返回指定位置的字符
去掉var str = "wangaaaallla"出现最多的字符,并统计出现次数

    let str = "wangaaaallla"
    function getStr (str) {
        let obj = {}
        for (var i = 0; i < str.length; i++) {
            console.log(obj[str.charAt(i)])
            if (obj[str.charAt(i)]) {
                obj[str.charAt(i)]++
            } else {
                obj[str.charAt(i)]=1
            }
        }
        let sum = 0
        let number
        for (let i in obj) {
            if (obj[i]>sum) {
                sum=obj[i]
                number=i
            }
        }
        console.log(number+'出现了====='+sum+'次');
    }
    getStr(str)

18, 获得并解析url中的参数 比如url: http://NaoNao.com/?product=shirt&color=blue&newuser&size=m#Hello

function UrlSearch() {
        var name,value;
        var args = {}
        var str='http://NaoNao.com/?product=shirt&color=blue&newuser&size=m#Hello'; //取得整个地址栏
        var num=str.indexOf("?")  // 18
        str=str.substr(num+1); //取得所有参数 product=shirt&color=blue&newuser&size=m#Hello
        var arr=str.split("&"); //各个参数放到数组里
        for(var i=0;i < arr.length;i++){
            num=arr[i].indexOf("=");  // 7 5 -1 4
            if(num>0){
                name=arr[i].substring(0,num);
                value=arr[i].substr(num+1);
                args[name] = value
            }
        }
        console.log(args)  // {product: "shirt", color: "blue", size: "m#Hello"}
        return args
    }
    UrlSearch()

substring() 方法用于提取字符串中介于两个指定下标之间的字符

var str="Hello world!"
undefined
str.substring(3, str.length)
"lo world!"

substr() 方法可在字符串中抽取从 start 下标开始的指定数目的字符

var str="Hello world!"
undefined
str.substring(3, str.length)
"lo world!"

slice() 方法可提取字符串的某个部分,并以新的字符串返回被提取的部分。

var str="Hello happy world!"
undefined
str.slice(6)
"happy world!"

19,JS实现排序
1,冒泡排序 bubble
思想: 让数组中的当前项和后一项比较,如果当前项大于后一项,则两者交换位置

let ary = [12, 8, 24, 16, 1]
    function  bubble (v) {
        for (let i = 0; i < v.length-1; i++) {   // 外层循环i控制比较的轮数
            for (let j = 0; j < v.length-i-1; j++) {  // 里层循环控制每一轮比较的次数
                if (v[j] > v[j + 1]) {
                    [v[j], v[j + 1]] = [v[j + 1], v[j]]
                }
            }
        }
        console.log(v)   // [1, 8, 12, 16, 24]
        return v
    }
    bubble(ary)

2,插入排序 insert

let ary = [12, 8, 24, 16, 1]
    function insert (v) {
        let arr = []
        arr.push(v[0])
        for (var i = 1; i < v.length; i++) {
            let temp = v[i]
            for (var j = arr.length - 1; j >= 0; j--) {
                let _a = arr[j]
                if (temp > _a) {
                    arr.splice(j + 1, 0, temp)
                    break
                }
                if (j === 0) {
                    arr.unshift(temp)
                }
            }
        }
        console.log(arr)    // [1, 8, 12, 16, 24]
        return arr
    }
    insert(ary)

你可能感兴趣的:(js常见面试题)