2021-12-05

js经典面试题

  /* console.log(a)  //undefined

        var a = 12

        function fn() {

            console.log(a)  //undefined

            var a = 13

        }

        fn()

        console.log(a)  //12 */

        /* console.log(a)  //undefined

        var a = 12

        function fn() {

            //注意:当前作用域里面没有a,就会到上一层作用域里面去寻找a

            console.log(a) //12

            a = 13

        }

        fn()

        console.log(a) //13 */

        /* // console.log(a)  //报错

        let a = 12  //let定义的变量,不存在提升

        function fn() {

            // console.log(a)  //报错,注意:只要是当前作用域里面的存在的变量,就不会去上层作用域找了

            let a = 13  //let定义的变量,不存在提升

        }

        fn()

        console.log(a) */


         /*// console.log(a)  //报错

        a = 12   // 定义变量可以不使用任何关键字,但是在定义该变量之前,不能使用该变量(这是一种不规范写法)

        function fn() {

            // console.log(a)  //报错

            let a = 13

        }

        fn()

        console.log(a) */

        /* var foo = 1

        function bar() {

            if (!foo) {

                var foo = 10  //这个foo会提升到当前作用域的顶部定义

            }

            console.log(foo)  //10

        }

        bar() */

        /* var foo = 1

        function bar() {

            if (!foo) {

                foo = 10

            }

            console.log(foo)   //1

        }

        bar() */

     /* var n = 0

        function a() {

            var n = 10   //11   12

            function b() {

                n++  //11  12

                console.log(n)  //11  12

            }

            b()

            return b

        }

        var c = a()

        c()

        console.log(n)  // 0  */

        /* var a = 10

        var b = 11

        var c = 12

        function test(a) {

            a = 1

            var b = 2   //方法里面的作用域b是2

            c = 3  //会将外层作用域的c的值换掉

        }

        test(10)

        console.log(a)  // 10

        console.log(b)  // 11

        console.log(c)  // 3 */

        // in关键字,用于检查一个属性是否包含在指定的对象中,如果包含就返回true

       /* if (!('a' in window)) {

            var a = 10

        }

        console.log(a)*/

        /* var a = 4

        function b(x, y, a) {

            console.log(a)  // 打印形参的值 3

            arguments[2] = 10  // 又将形参a改成而来 10

            console.log(a)  // 10

        }

        a = b(1, 2, 3)  //b方法,没有返回任何内容,默认返回undefined

        console.log(a)  //undefined */

        /* var a = 9

        function fn() {

            // 1 2

            a = 0  

            return function (b) {

                //     5 + 1

                //     5 + 0

                return b + a++

            }

        }

        var f = fn()

        console.log(f(5))   //5

        console.log(fn()(5))  //5

        console.log(f(5))   //6

        console.log(a) //2 */

        /* var ary = [1, 2, 3, 4]

        function fn(ary) {

            ary[0] = 0   // 修改了原数组中第一位的值

            ary = [0]    // arr形参重新赋值一个新的数组

            ary[0] = 100 // 形参arr再修改第一个位置的值,就跟原数组没关系

            return ary

        }

        var res = fn(ary)   // [100]

        console.log(ary)    // [0,2,3,4]

        console.log(res)   // [100] */

        /* //          10->11

        function fn(i) {

            //               30

            return function (n) {

                console.log(n + i++)  //41

            }

        }

        var f = fn(10)

        f(20)   //30

        fn(20)(40)  //60

        fn(30)(50)  //80

        f(30) //41 */

        /* var num = 10  //60  65

        var obj = { num: 20 }  // 30

        //                   20  21

        obj.fn = (function (num) {

            //this->window

            this.num = num * 3

            num++ // 21

            //               10

            return function (n) {

                this.num += n  // 30

                num++  //22 23

                console.log(num)  //22 23

            }

        })(obj.num)

        var fn = obj.fn

        console.log(fn)  //function(n){this.num +=n;num++;console.log(num)}

        fn(5)  // 22

        obj.fn(10) //23

        console.log(num, obj.num) //65 30 */

        /* var fullName = 'language'

        var obj = {

            fullName: 'javascript',

            prop: {

                getFullName: function () {

                    return this.fullName

                }

            }

        }

        console.log(obj.prop.getFullName())  // undefined

        // 将getFullName方法传给test

        var test = obj.prop.getFullName

        console.log(test())  // language */


rest参数

 // 方法的形参前面添加...,就是方法的rest参数

        //...xxx 是函数的rest参数,用于接收剩余的实参,注意:通常情况下rest参数放在最后面

        //rest参数,解决了函数中arguments对象不是数组类型的缺陷

        function fun1(a,b,c,...args){

            console.log(a,b,c);

            console.log(args);

            let arr = args.map(r=>r*2)

            console.log(arr);

            // console.log(arguments);

        }

        fun1(100,200,300)

        fun1(100)

        fun1(10,20,30,40,50,60,70,80)

        console.log('------------------------');

        // 在定义函数时,可以给参数设置默认值

        function fun2(a,b=200,c=300){

            console.log(a,b,c);

        }

        fun2(1,2,3)

        fun2(1,2)

        fun2(1)

        fun2()



展开运算符

let arr1 = [11,22,33]

        let arr2 = [44,55,66]

        let arr3 = arr1.concat(arr2)

        console.log(arr3);

        // ...在这里就是展开运算符,在这里,展开运算符用于展开数组中的所有成员。

        let arr4 = [...arr1,...arr2]

        console.log(arr4);

        console.log('---------------------------');

        let obj1 = {

            a:100,

            b:200

        }

        let obj2 = {

            c:300,

            d:400,

            a:500

        }

        // ...在这里,用于将对象的所有属性展开,并返回一个全新的对象

        let obj3 = {...obj1,...obj2}

        console.log(obj3);    


结构赋值

 //定义变量

        let no = 1001

        let name = '周杰伦'

        let age = 30

        let sex = '男'

        //定义对象

        let stu1 = {

            //属性名:属性值

            //这里的属性值是上面定义的变量保存的值

            no:no,

            name:name,

            age:age,

            sex:sex

        }

        console.log(stu1);

        console.log('-----------------');

        //对象的属性名和属性值的标识相同时,可以省略属性值

        let stu2 = {

            // 是no:no的简写

            no,

            name,

            age,

            sex

        }

        console.log(stu2);

        console.log('--------------------------------------');

        let stu3 = {

            username:'周杰伦',

            userage:30,

            usersex:'男',

            car:{

                carName:'奔驰',

                carPrice:'100W'

            }

        }

        //过去我们这样写

        // let username = stu3.username

        // let userage = stu3.userage

        let usersex = stu3.usersex

        //现在我们这样写(解构赋值)

        // usersex:usersex2 表示在解构的时候对变量名进行重命名

        let {username,userage,usersex:usersex2} = stu3

        console.log(username,userage,usersex,usersex2);

        console.log('-----------');

        // let {car} = stu3

        // let {carName,carPrice} = car

        let {car:{carName,carPrice}} = stu3  //这一行代码,最终会编译成下面的两行代码

        // let carName = stu3.car.carName

        // let carPrice = stu3.car.carPrice

        console.log(carName,carPrice);

        let arr = [11,22,33,44,55]

        // 解构数组中的元素,采用的[]

        let [a,b] = arr

        console.log(a,b);


原型对象

 // 构造函数(类)有原型对象,其实就是构造函数身上的一个自带属性,这个属性是:prototype

        // 对象也有原型对象,其实就是对象身上的一个自带属性,这个属性是:__proto__

        // 所有同类型的对象身上的原型对象属性,都指向类的原型对象属性。

        // 类和对象的原型对象身上挂的方法,对象可以直接使用,不需要经过原型对象。

        function Student(name,age,sex){

            this.name = name

            this.age = age

            this.sex = sex

            // 如果将方法直接定义在类里面,将来根据这个类创建的每个对象,都要创建自己独立的这些方法

            // 如果要创建很多对象,对内存的开销会很大。

            /* this.sayHi = function(){

                console.log(`Hi!我叫${this.name},今年${this.age}岁,性别是${this.sex}`);

            }

            this.study = function(time){

                console.log(`Hi!我叫${this.name},我每天学习${time}小时`);

            }

            this.play = function(time){

                console.log(`Hi!我叫${this.name},我每天玩${time}小时`);

            } */

        }

        // 我们可以将类的方法,添加到类的原型对象身上

        Student.prototype.sayHi = function(){

            console.log(`Hi!我叫${this.name},今年${this.age}岁,性别是${this.sex}`);

        }

        Student.prototype.study = function(time){

            console.log(`Hi!我叫${this.name},我每天学习${time}小时`);

        }

        Student.prototype.play = function(time){

            console.log(`Hi!我叫${this.name},我每天玩${time}小时`);

        }

        let s1 = new Student('张三',20,'男')

        let s2 = new Student('李四',22,'女')

        let s3 = new Student('王五',24,'男')

        // 查了Student类的原型对象

        // console.log(Student.prototype);

        // 查看三个对象的原型对象 -- 你会发现,长得一样。

        // console.log(s1.__proto__);

        // console.log(s2.__proto__);

        // console.log(s3.__proto__);

        s1.sayHi()

        s1.study(8)

        s1.play(3)

        console.log('------------------');

        s2.sayHi()

        s2.study(6)

        s2.play(6)

        console.log('------------------');

        s3.sayHi()

        s3.study(10)

        s3.play(1)

class定义类

 // ES6之前,定义类型的方式

        function Student(name,age,sex){

            //定义属性

            this.name = name

            this.age = age

            this.sex = sex

            //定义方法

            this.sayHi = function(){

                console.log(`我是学生,姓名是${this.name},年龄${this.age}岁,性别是${this.sex}`);

            }

        }

        let s1 = new Student('张三',20,'男')

        s1.sayHi()

        // 从ES6以后,添加了class关键字,定义类型

        // 注意:该语法的兼容性不高,ie9以下都不支持,部分其他低版本的浏览器也存在兼容性问题。

        class Teacher{

            //构造函数(通过构造函数,给属性赋值)

            constructor(name,age,sex){

                this.name = name

                this.age = age

                this.sex = sex

            }

            //定义方法 (将方法定义在类身上)

            sayHi = function(){

                console.log(`我是老师,姓名是${this.name},年龄${this.age}岁,性别是${this.sex}`);

            }

            //(将方法定义在原型对象上)

            sayHello(){

                console.log(`Hello,姓名是${this.name},年龄${this.age}岁,性别是${this.sex}`);

            }

        }

        let t1 = new Teacher('李四',22,'女')

        console.log(t1);

        t1.sayHi()

        t1.sayHello()



ES6的继承方式


// 定义动物类

        class Animal{

            // 定义动物类的构造函数

            constructor(nickName,sex,age){

                // 利用构造函数给动物的属性赋值

                this.nickName = nickName

                this.sex = sex

                this.age = age

            }

            // 定义方法,并将方法添加到原型上

            sayHi(){

                console.log(`Hi!我叫${this.nickName},今年${this.age}岁,我是${this.sex}`);

            }

            eat(str){

                console.log(`我喜欢吃${str}`);

            }

            sleep(time){

                console.log(`我每天睡${time}个小时`);

            }

        }

        // 创建一个动物对象

        let a1 = new Animal('佩奇','女生',6)

        a1.sayHi()

        a1.eat('蛋糕')

        a1.sleep(12)

        console.log('-----------------------------------------');


        // 定义狗狗类

        // class关键字,定义类;extends关键字继承类,

        // 采用这种方式,Animal的方法,此刻已经全部继承过来了。

        class Dog extends Animal{

            constructor(nickName,sex,age,type){

                //调用父类的构造函数,而且必须放在子类构造函数的最上方

                super(nickName,sex,age)

                this.type = type

            }

            play(){

                console.log(`我一只${this.type},我会玩飞盘`);

            }

        }

        let d1 = new Dog('旺财','男生',6,'拉布拉多')

        d1.sayHi()

        d1.eat('牛肉')

        d1.sleep(8)

        d1.play()



核心案例

  //定义DVD类

        function DVD(id,name,count,state,price){

            //编号

            this.id = id

            //名称

            this.name = name

            //人气值

            this.count = count

            //状态值

            this.state = state

            //日租金

            this.price = price

        }

        //DVD计算总租金的方法

        DVD.prototype.totalPrice = function(days){

            let money = this.price*days //定义变量,用于保存总租金

            // 租用天数超过10天,打八折

            // 租用天数超过5天,打9折

            if(days>=10){

                money *= 0.8

            }else if(days>=5){

                money *= 0.9

            }

            return money

        }

        //定义一个dvd管理对象

        let dvdManager = {

            //定义dvd数组

            dvds:[

                new DVD(1001,'非诚勿扰',5,false,1.5),

                new DVD(1002,'超级战舰',3,true,2),

                new DVD(1003,'火星救援',8,true,1),

                new DVD(1004,'上海堡垒',2,false,0.5)

            ],

            //查看dvd信息的方法

            show:function(){

                let str = "编号 DVD名称 人气值 日租金 状态\n"

                this.dvds.forEach(d=>{

                    str+=`${d.id} ${d.name} ${d.count} ¥${d.price} ${d.state?'借出':'未借'}\n`

                })

                alert(str)

            },

            //添加dvd信息的方法

            add:function(){

                //输入dvd的名称

                let name = prompt('请输入DVD的名称:')

                // 检查输入的dvd是否已经存在了

                if(this.getDVDByName(name)) {

                    alert('您输入的DVD已经存在!')

                    //使用递归

                    arguments.callee.call(this)

                    return //返回当前方法

                }

                // 输入dvd的日租金

                let price = parseFloat(prompt('请输入DVD的日租金:'))

                //创建dvd对象

                let dvd = new DVD(parseInt(Math.random()*9000)+1000,

                name,0,false,price)

                //将dvd对象添加到数组中

                this.dvds.push(dvd)

                alert('添加成功!')

            },

            //删除dvd的方法

            delDVD:function(){

                // 输入dvd的名称

                let name = prompt('请输入DVD的名称:')

                // 根据dvd的名称,获取dvd对象

                let dvd = this.getDVDByName(name)

                // 判断dvd是否存在

                if(dvd){

                    //判断dvd的状态是否为借出

                    if(dvd.state){

                        alert('该DVD已经借出,无法删除!')

                    }else{

                        // 根据dvd的名称,找出该dvd的下标

                        let index = this.dvds.findIndex(d=>d.name===name)

                        // 根据下标删除

                        this.dvds.splice(index,1)

                        alert('该DVD删除成功!')

                    }

                }else{

                    alert('您输入的DVD不存在!')

                }

            },

            //根据dvd的名称,返回dvd对象

            getDVDByName:function(name){

                return this.dvds.find(r=>r.name===name)

            },

            //借出DVD的方法

            jieDVD:function(){

                // 输入dvd的名称

                let name = prompt('请输入DVD的名称:')

                // 根据dvd的名称,获取dvd对象

                let dvd = this.getDVDByName(name)

                // 判断dvd是否存在

                if(dvd){

                    // 判断dvd的状态是否为借出

                    if(dvd.state){

                        alert('该DVD已经借出,您可以换别的!')

                    }else{

                        //设置该DVD的状态为借出

                        dvd.state = true

                        //设置该DVD的借出次数加1

                        dvd.count++

                        alert('成功借出!')

                    }

                }else{

                    alert('对不起!您输入的DVD不存在!无法借出!')

                }

            },

            //归还DVD的方法

            huanDVD:function(){

                // 输入dvd的名称

                let name = prompt('请输入DVD的名称:')

                // 根据dvd的名称,获取dvd对象

                let dvd = this.getDVDByName(name)

                // 判断dvd是否存在

                if(dvd){

                    // 判断dvd的状态是否为借出

                    if(dvd.state){

                        dvd.state = false  //重新设置dvd的状态为未借

                        let days = Math.ceil(prompt('请输入租用天数:'))

                        // 调用dvd计算租金的方法

                        let money = dvd.totalPrice(days)

                        alert('归还成功!您需要支付'+money+'元租金')

                    }else{

                        alert('对不起!您输入的DVD未借出!无法归还!')

                    }

                }else{

                    alert('对不起!您输入的DVD不存在!无法归还!')

                }

            },

            //系统主菜单

            menu:function(){

                let no = prompt('*****迷你DVD管理系统*****\n1.查看DVD 2.租售DVD 3.归还DVD 4.添加DVD 5.删除DVD 0.退出系统')

                switch(no){

                    case '1':

                        // 调用查看DVD信息的方法

                        this.show()

                        break;

                    case '2':

                        // 调用借出DVD的方法

                        this.jieDVD()

                        break;

                    case '3':

                        // 调用归还DVD的方法

                        this.huanDVD()

                        break;

                    case '4':

                        // 调用添加DVD的方法

                        this.add()

                        break;

                    case '5':

                        // 调用删除DVD的方法

                        this.delDVD()

                        break

                    default:

                        alert('成功退出系统!欢迎下次使用!')

                        return  //跳出当前方法

                }

                // 递归调用菜单方法,并重新指定this的指向。

                arguments.callee.call(this)

            }

        }

        // 调用dvd管理对象的主菜单方法

        dvdManager.menu()


复习 arguments

 //arguments是函数内部的一个对象,该对象可以接收函数的所有参数

            // 该对象有一个方法callee方法,该方法指向当前函数本身

例如:

function fn1(){

 console.log(arguments);

            console.log(arguments.callee===fn1);

        }

        fn1(100,200,300,400)


结果为:


箭头函数中的this


 let lh = {

            name: '鹿晗',

            age: 20,

            //朋友

            friend: {

                name: '关晓彤',

                age: 18,

                //车

                getCar: function () {

                    return {

                        name: '奔驰',

                        age: 3,

                        sayHi: function () {

                            // 方法里面的this,指向方法的调用者,比如:obj.fn,那么fn里面的this指向obj

                            console.log(`一辆${this.name}车,车龄是${this.age}年`);

                        },

                        sayHello: () => {

                            // 箭头函数中没有this,如果使用了this,会向上一层方法寻找this,如果上面已经没有方法了

                            // 就指向window对象

                            console.log(`一辆${this.name}车,车龄是${this.age}年`);

                        }

                    }

                }

            }

        }

        lh.friend.getCar().sayHi()

        lh.friend.getCar().sayHello()


结果为:

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,544评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,430评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,764评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,193评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,216评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,182评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,063评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,917评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,329评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,543评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,722评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,425评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,019评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,671评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,825评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,729评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,614评论 2 353

推荐阅读更多精彩内容

  • JS内置对象的Math属性 概念:Math是一个内置对象,它拥有一些数学常数属性和数学函数方法,它不是一个函数对象...
    A阿a阅读 342评论 0 0
  • call apply bind // call apply bind 更改方法里面this的指向 let ...
    A阿a阅读 210评论 0 0
  • 使用对象制作计算器 //定义一个计算器对象 let calc = { //定义计算器的属性 ...
    A阿a阅读 185评论 0 0
  • 1、JavaScript初识 1. 说几条 JavaScript 的基本规范? (1)一个函数作用域中所有的变量声...
    没糖_cristalle阅读 436评论 0 0
  • 1 - 编程语言 1.1 编程 编程:就是让计算机为解决某个问题而使用某种程序设计语言编写程序代码,并最终得到结果...
    Scincyc阅读 278评论 0 0