面试题 js

1 数据类型

基本数据类型有
Number | String | Boolean | Null | Undefined | Symbol(ES6新增数据类型) | bigInt
引用数据类型统称为Object类型,细分的话有
Object | Array | Date | Function | RegExp

*区别*
基本数据类型的数据直接存储在栈中;
引用数据类型的数据存储在堆中,在栈中保存数据的引用地址,这个引用地址指向的是对应的数据,以便快速查找到堆内存中的对象。

顺便提一句,栈内存是自动分配内存的。而堆内存是动态分配内存的,不会自动释放。所以每次使用完对象的时候都要把它设置为null,从而减少无用内存的消耗

2 判断数据类型有几种方法

  • 1 typeof 缺点:无法分辨是null, Array还是Object
typeof null [] {} 11 NaN undefined Object
'Object' 'Object' 'Object' 'Number' 'Number' 'undefined' 'function'
  • 2 instanceof (instance: 例子) 相当于.__proto__.constructor===
    function Foo() { }
    var f1 = new Foo();
    var d = new Number(1)


    console.log(f1 instanceof Foo);// true  f1.__proto__.constructor===Foo
    console.log(d instanceof Number); //true
    console.log(123 instanceof Number); //false   -->不能判断字面量的基本数据类型

判断[]
var arr =[]
console.log(arr instanceof Array)
  • 3 Object.prototype.toString.call() 相当于Undefined.prototype.toString(伪代码)
//.call()
var person = {fullName:function(){return this.firstName + " " this.lastName;}}
var person1 = { firstName:"Bill", lastName: "Gates", }
person.fullName.call(person1);  // 将返回 "Bill Gates"
console.log(Object.prototype.toString.call(undefined)); // "[object Undefined]" 
console.log(Object.prototype.toString.call(null)); // "[object Null]" 
console.log(Object.prototype.toString.call(123)); // "[object Number]" 
console.log(Object.prototype.toString.call("abc")); // "[object String]" 
console.log(Object.prototype.toString.call(true)); // "[object Boolean]" 

2 Foo.getName()的故事

    function Foo() {
	  //相当于window.getName
      getName = function () {        console.log(1);      };
      console.log('this is' + this)
      return this;
    }
	//为Foo添加this.getName一个方法
    Foo.getName = function () {      console.log(2);    };
    Foo.prototype.getName = function () {  console.log('baidu' && 'google');  };
	//变量赋值
    var getName = function () {      console.log(4);    };
	//变量声明
    function getName() {      console.log(5);    }

    // 请写出一下的输出结果
    Foo.getName();  //2
    getName();  //4
    Foo().getName();  //Foo(): getName赋值 | this is window | windows   1
    getName();   //1

	//运算符的优先级 
	//【 obj.X = new X() =  X() > new X】
	//结合性  obj.X = new X() =  X()从左往右  new X从右往左

    new Foo.getName();  //2  new (Foo.getName)();

    new Foo().getName();  //(new Foo()).getName()
	//this is [object Object] (因为没在里面this.getName 去找原型)  google  

    new new Foo().getName(); //  new ( (new Foo()).getName ) ()
	//this is [object Object]  (将getName作为构造函数再次new)  google
&& 和 ||
1 && x 运行x   0 && x 运行0
1 || x 运行1   0 || x 运行x

3 数组去重

var arr = [1, 1, 8, 8, 12, 12, 15, 15, 16, 16];
//es5
function unique(arr) {
    for (var i = 0; i < arr.length; i++) {    // 首次遍历数组
        for (var j = i + 1; j < arr.length; j++) {   // 再次遍历数组
            if (arr[i] == arr[j]) {          // 判断连个值是否相等
                arr.splice(j, 1);           // 相等删除后者
				// array.splice(index, howmany, item1, ....., itemX)
				// index指定位置添加/删除,使用负值指定从数组末尾开始的位置。
				// howmany要删除的项目数。如果设置为 0,则不会删除任何项目。
                j--;
            }
        }
    }
    return arr
}

//es6
function unique (arr) { return Array.from(new Set(arr)) }

//es5其他方法
创建新[], 新arr.indexOf() 新arr.includes()
不新[]
function unlink(arr) {
    return arr.filter(function (item, index, arr) {
        //当前元素,在原始数组中的第一个索引===当前索引值,否则返回当前元素
        return arr.indexOf(item) === index;
    });
}

4 深浅拷贝

//浅拷贝 对象只有一层
    // 方式1
    function shallowClone1(o) {
      let obj = {}

      for (let i in o) {
        obj[i] = o[i]
      }
      return obj
    }

    // 方式2
    var shallowClone2 = { ...obj1 }

    // 方式3
    var shallowClone3 = Object.assign({}, obj1)

	
// 深拷贝,对象里还有对象
	// 简易版  
    function deepClone(o) {
      let obj = {}
      for (var i in o) {
        // if(o.hasOwnProperty(i)){
        if (typeof o[i] === "object") {
          obj[i] = deepClone(o[i])
        } else {
          obj[i] = o[i]
        }
        // }
      }
      return obj
    }

	// 简易版存在的问题:参数没有做检验,传入的可能是 Array、null、regExp、Date
    // 有可能碰到循环引用问题  var a = {}; a.a = a; clone(a);//会造成一个死循环
    // 循环检测
    // 继续升级
    function deepClone4(o, hash = new map()) {
      if (!isObject(o)) return o//检测是否为对象或者数组
      if (hash.has(o)) return hash.get(o)
      let obj = Array.isArray(o) ? [] : {}

      hash.set(o, obj)
      for (let i in o) {
        if (isObject(o[i])) {
          obj[i] = deepClone4(o[i], hash)
        } else {
          obj[i] = o[i]
        }
      }
      return obj
    }

5 身份证正则

RegExp 对象定义

var reg = /^\d{17}[X\d]$/

[]    匹配[]中的任何一位字符
^和$  同时使用 严格匹配-必须跟书写的正则内容完全相同, 不能长或短
{X}   匹配X个 n的组合

RegExp 对象方法

test()  检索字符串中的指定值。返回值是 true 或 false。
exec()  检索字符串中的指定值。返回值是被找到的值或null。

console.log(reg.test("146284378426327953"))  //true

5 i++

var i=0
console.log(i++)  //0
console.log(i)      //1

6 concat

var arr1 = [1, 2, 12]
var arr2 = [3, 5]
arr1.concat(arr2)  //不改变arr1
arr1.sort()         //1 12 2 
alert(arr1.join(";"))  //1;12;2

7 new()到底做了什么?

function Person () {
    this.name = name;
    this.age = age;
    this.sex = sex
 
    this.sayName = function () {
        return this.name;
    };
}
Person.prototype.sayAge = function(){eturn this.age;}

var obj = new Person("tom", 21, "famle");
 

使用关键字new创建新实例对象经过了以下几步:

1 创建一个空对象{}
2 继承原型, 将新对象的_proto_指向构造函数的prototype对象
3 继承属性方法, Person.call(obj) (this指向新对象 Person的this 指向-> obj)
4 返回新的对象

8 "use strict"是什么,优缺点

严格模式
优点: 代码更严谨,安全
缺点: 项目中打包压缩js时, 会将严格模式与非严格模式js代码混合, 导致非严格模式js代码出错

9 将url转化为对象

var url = 'https://www.so.com/s?q=es6&src=srp&fr=hao_360'

var str = url.split('?')[1]
var temp = str.split('&')
var obj = {}
for(var i=0; i<temp.length;i++){
  var t = temp[i].split('=')
  obj[t[0]]=t[1]
}
console.log(obj) //结果{ q:'es6',src:'srp',fr:'hao_360'}

10 字符串中出现最多的字符

var str = 'aaaabbbbbbccccccc'
var obj = {}
for (var i = 0; i < str.length; i++) {
  var char = str.charAt(i)
  if (obj[char]) obj[char]++
  else obj[char] = 1
}
console.log(obj) //{a: 4, b: 6, c: 7}

var sum = 0 , name = null
for (var i in obj) {
  if (obj[i] > sum) {
    name = i
    sum = obj[i]
  }
}
console.log(name+":"+sum) //c:7

11 创建对象的方式

1 {}与new Object()
2 new 构造函数()
3 Object.create(原型)
	
	//父类
	function Person(name,age){}
	//子类
	function Student(name){}
	//继承
	Student.prototype = Object.create(Person.prototype) //=new Person();

不能直接Student.prototype = Person.prototype(引用类型赋值)
等价于	Student.prototype = new Person();

12 判断图片加载完成

1 onload事件
    <img id="img1"src="http://pic1.win4000.com/wallpaper/f/51c3bb99a21ea.jpg">
		
    <script type="text/javascript">
        img1.onload =function() {}
    </script>

2 onreadystatechange//在xmlhttp用过, readystate准备状态,readystatechange准备状态变化
    img1.onreadystatechange =function() {
        if(img1.readyState=="complete"||img1.readyState=="loaded"){}
	}

3 轮询节点属性complete
    var timer = setInterval(function() {
		
                if(img.complete) {  clearInterval(timer)  }
		
                }, 50)

13 对象作为key值

var a={},
b={key:'b'},
c={key:'c'};

a[b]=123;
a[c]=456;

console.log(a[b]);//为什么是456? 

因为键名称只能是字符串,b/c单做键会调用toString得到的都是[object Object],a[b],a[c]都等价于a["[object Object]"],那不就是更新[object Object]这个键的值了

14 事件委托(代理)

1 事件委托是利用事件的冒泡原理来实现的,

2 大致可以分为三个步骤:  
  (1) 确定要添加事件元素的父级元素;
  (2) 给父元素定义事件,监听子元素的冒泡事件;
  (3) 使用 event.target 来定位触发事件冒泡的子元素。

3 当子元素是动态的必须用事件委托

4 优点
  (1)节约内存 (事件处理程序都是对象,对象越多越会占用内存)
  (2)动态绑定事件

15 隐式转换

=>数字
console.log(true+1) //2
console.log(undefined+1) //NaN

=>字符串
console.log("12"+true) //12true

console.log(typeOfnull) //Object

16 websocket 与 ajax

websocket  长连接    全双工,可以从服务器推送更新信息到客户端
ajax       短链接    只能客户端发起请求,若想获得服务器信息更新只能轮询
1 大小不同
  cookie 4k  session 无限制  localStorage和sessionStorage 5M

2 有效期不同
  cookie   document.cookie="url=http:...; max-age=" + 30*24*60*60;
  //max-age默认为-1,即关闭浏览器后失效, 为0时,删除cookie

  session 永久
  
  localStorage  永久
  
  sessionStorage  页面关闭
  
3 存储位置
  cookie  localStorage  sessionStorage  客户端
  session  服务器
  
HTML4的cookie有很多缺点(小,随HTTP发送占用带宽),
HTML5不再使用它,转而使用改良后的Web Storage(localStorage和sessionStorage)

18 将函数的arguments转化为数组

1 arguments是对象 => Array.from(arguments)
2 [...arr] 是将伪数组转化为数组

19 获取某元素下所有节点

document.getElementById('box').getElementByTagName("*")

20 显示到2023年的剩余时间

<script>
  var newyear = new Date("1/1/2023");
  function fn() {
    var now = new Date();
    var time = new Date(newyear.getTime() - now.getTime());
    document.getElementsByTagName("timer")[0].innerHTML = 
         time.getMonth()+1+"月"+time.getDay()+"日"+time.getHours()+"小时"
         +time.getMinutes()+"分"+time.getSeconds()+"秒";
  }
  setInterval(fn, 500);
</script>

<body>
    <timer></timer>
</body>
posted @ 2022-02-23 22:36  波吉国王  阅读(26)  评论(0编辑  收藏  举报