美团面经学习(一)

160 阅读5分钟
  • 判断回文字符串
  • 驼峰命名转下划线命名
  • 箭头函数和普通函数区别
  • rem原理
  • js原型,js继承
  • js链表,有什么特点,运用场景
  • 原生网络请求是如何实现的
  • 跟缓存相关的http请求头
  • for in for of的区别
  • instanceof和typeof的区别

判断回文字符串

方法一:字符串反转比较

const _isPalindrome = string => {
    // 补全代码
    string=string.split('')

    let str1=string.reverse().join('')
    if(str1==string) return true
    else{
        return false
    }

}

这里有一个更省略的做法,就是我们可以不用join,然后利用==只比较值相等,那我只需要reverse就可以了。不需要最后的join,比较字符串和数组的值是否相等即可

方法二:左右指针

const _isPalindrome = string => {
        // 补全代码
        let left=0
        let right=string.length-1
        while(left<right){
            if(string[left]===string[right]){
                left++
                right--
            }else{
                return false
            }
        }
        return true
    }

===和==的区别

  • ===:严格相等,值和类型都必须一样
  • ==相等,值一样就行

驼峰命名转下划线命名

方法一:正则匹配

function camelToUnderscore(str) {
  return str.replace(/([A-Z])/g, "_$1").toLowerCase();
}

方法二:遍历数组,如果是大写字母,就将其转换成小写并在前面加上下划线

function camelToUnderscore(str) { 
    let result = ''; 
    for (let i = 0; i < str.length; i++) { 
        const char = str[i]; 
        if (char === char.toUpperCase()) { 
        result += '_' + char.toLowerCase(); 
        } else { 
        result += char; 
    } } 
    return result; 
}

下划线转驼峰: 方法一:分割字符串之后逐个转换

function cssStyle2DomStyle(sName) {
             // 填写JavaScript
    let arr1=sName.split('_').filter(item=>item)
    let str=''
    arr1.forEach((item,index)=>{
        item=item[0].toUpperCase()+item.slice(1)
        console.log(item)
        str+=item
    })
    str=str[0].toLowerCase()+str.slice(1)
    return str
 }

.filter(item=>item)

可以过滤空值,因为在JavaScript中,空字符串、null、undefined等被视为假值(falsy values),因此当filter方法传入的函数返回假值时,该元素就会被过滤掉。

方法二:正则匹配,将-x转换成X,并判断第一个字母不大写

 function cssStyle2DomStyle(sName) {
   return sName.replace(/-([a-z])/g, function (match, letter,index) {
    if(index==0) return letter
    return letter.toUpperCase();
  });
}

replace

str.replace(ext, function(match,letter,index,s){})

  • 匹配到的字符串
  • 回调函数返回替换的值,如果没有返回,默认为undefined 
  • 匹配字符串的对应索引位置
  • 原始字符串

例如上述输出: image.png

箭头函数和普通函数区别

  1. 语法:
  • 箭头函数使用箭头 => 来定义函数,语法简洁。例如:(x, y) => x + y
  • 普通函数使用关键字 function 来定义函数,语法相对较长。例如:function(x, y) { return x + y; }
  1. this 的指向:
  • 箭头函数没有自己的 this,它会捕获所在上下文的 this 值。换句话说,在箭头函数内部使用的 this 是外层作用域的 this
  • 普通函数有自己的 this,其值取决于函数如何被调用。
window.url='00'
let a={ url:'11'}
function init(){
  console.log(this.url)
}

  init2=()=>{
  console.log(this.url)
}

init()
a.init=init
a.init()

a.init2=init2
a.init2()     
a.init2()  

结果:可以看到箭头函数并没有打印和函数一样的结果,因为他没有自己的this,所以两次访问的都是外部window对象

image.png

  1. arguments 对象:

箭头函数没有自己的 arguments 对象,它会捕获所在上下文的 arguments 值。

  • 普通函数有自己的 arguments 对象,其中包含了传递给函数的参数。

arguments 对象是一个类数组对象,它包含了函数被调用时传递给函数的所有参数

  1. 构造函数:
  • 箭头函数不能用作构造函数,不能使用 new 关键字来调用。
  • 普通函数可以用作构造函数,可以使用 new 关键字来创建实例对象。

为什么

  • 没有自己的 this 绑定:因为构造函数当中我们期望this指向新创建的对象,但是箭头函数没有自己的this
  • 没有prototype:就无法被用于创建基于原型的继承关系

函数对象的一些属性

  • caller

用于访问调用当前函数的函数。它是一个非标准属性,不建议在生产代码中使用,因为它已经被废弃,并且在严格模式下会导致错误。

  • new.target

用于检查函数是否使用new关键字调用new.target,是就引用这个函数

function Foo() {
  if (!new.target) throw "Foo() must be called with new";
  console.log("Foo instantiated with new");
}

Foo(); // throws "Foo() must be called with new"
new Foo(); // logs "Foo instantiated with new"

rem原理

  • 相对于根元素字体大小的单位
  • 如果根元素的字体大小是16px,1rem就等于16px;如果根元素的字体大小改变为20px,1rem也会相应地变为20px

js原型,js继承

构造函数、原型、实例对象的关系

每一个构造函数都有一个原型对象,原型有一个属性指回构造函数,实例也有一个内部指针指向原型

原型链

如下,我们创建了一个Person的构造函数,我们说只要是构造函数就有原型对象,并且我们创建了一个对象实例,那么只要是对象就有_proto_,通过_proto_牵线我们就可以使用原型上的一些方法,但是如果原型找不到,他不会就此停止,因为我们说原型也是一个对象,他也有_proto_指向他的原型对象,就这么一直寻找直至最后返回null,这样一层一层形成链式结构就称为原型链

js继承

原型继承

比如我们创建了一个数组对象,那么这个数组对象实例就可以继承数组原型上一些方法,所以我们才能够没有写这些方法但是可以使用这些方法,因为这些方法都是原型上有的方法

构造继承

构造函数来创建对象,并且可以在构造函数的原型对象上定义方法和属性,以便所有由该构造函数创建的对象都可以共享这些方法和属性。

// 使用构造函数和原型实现继承
function Person(name) {
  this.name = name;
}

Person.prototype.sayHello = function() {
  console.log(`Hello, my name is ${this.name}.`);
};

function Student(name, grade) {
  //调用Person这个构造函数
  Person.call(this, name);
  this.grade = grade;
}
//建一个新的对象,该对象的原型指向 Person.prototype。
//这意味着 Student.prototype 对象可以访问 Person.prototype 对象中定义的属性和方法。
//通过这种方式,Student 对象可以继承 Person 对象的属性和方法。
Student.prototype = Object.create(Person.prototype);

//将 Student 构造函数重新指向 Student.prototype。
//因为在前一步中我们将 Student.prototype 设置为 Object.create(Person.prototype),
//这会导致 Student.prototype.constructor 指向 Person 构造函数。
//通过将它重新指向 Student 构造函数,我们可以确保构造新对象时使用正确的构造函数。
Student.prototype.constructor = Student;

Student.prototype.sayGrade = function() {
  console.log(`I am in grade ${this.grade}.`);
};

let stu=new Student('梨花',100);
stu.sayHello()

打印结果:

image.png

类继承

extends

// 使用 ES6 中的类和继承
class Person {
  constructor(name) {
    this.name = name;
  }

  sayHello() {
    console.log(`Hello, my name is ${this.name}.`);
  }
}

class Student extends Person {
  constructor(name, grade) {
    super(name);
    this.grade = grade;
  }

  sayGrade() {
    console.log(`I am in grade ${this.grade}.`);
  }
}

js链表,有什么特点,运用场景

特点:

  • 非连续存储: 链表中的元素在内存中不是连续存储的,而是通过指针相互连接起来的。
  • 动态大小: 链表在创建时不需要预先指定大小,可以根据需要动态增加或删除元素。
  • 插入和删除高效: 由于链表中的元素不需要连续存储,因此在插入和删除元素时,不需要像数组那样移动其他元素,所以操作效率较高。
  • 访问速度较慢: 相对于数组,链表的访问速度较慢,因为访问链表中的任何一个元素都需要从头开始逐个遍历。

应用场景:动态内存分配、动态数据集合

原生网络请求是如何实现的

XMLHeepRequest

  • 声明new XMLHttpRequest
  • onreadystatechange
  • readySate
  • open

image.png

//创建对象
xmlhttp=new XMLHttpRequest();

//发送
xmlhttp.open("GET","url",asynctrue,false));
xmlhttp.send();

xmlhttp.open("GET","/try/ajax/demo_get2.php?fname=Henry&lname=Ford",true); 
xmlhttp.send();

//如果需要像 HTML 表单那样 POST 数据
//请使用 setRequestHeader() 来添加 HTTP 头。
//然后在 send() 方法中规定您希望发送的数据:
//通过调用`setRequestHeader`方法设置`Content-type`请求头的目的是告诉服务器发送的数据的格式和类型。
xmlhttp.open("POST","/try/ajax/demo_post2.php",true); 
xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded"); 
xmlhttp.send("fname=Henry&lname=Ford");


//接收json
//事件绑定
xhr.onreadystatechange = function(){
    if(xhr.readyState === 4){
        if(xhr.status >= 200 && xhr.status < 300){
            // console.log(xhr.response);
            // result.innerHTML = xhr.response;
            // 1. 手动对数据转化
            let data = JSON.parse(xhr.response);
            // 2. 自动转换,先设置xhr.responseType='json'
            console.log(xhr.response);
            result.innerHTML = xhr.response.name;
        }
    }
}


Fetch

fetch('https://api.example.com/data')
  .then(function(response) {
    if (!response.ok) {
      throw new Error('Network response was not ok');
    }
    return response.text();
  })
  .then(function(data) {
    // 对响应数据进行处理
    console.log(data);
  })
  .catch(function(error) {
    // 处理请求过程中的错误
    console.log('Fetch error: ', error);
  });

跟缓存相关的http请求头

  • If-None-Match: 用于条件请求,客户端可以在请求中发送之前获取到的实体标签(ETag),服务器会根据这个值判断资源是否有更新,如果没有更新,服务器会返回 304 Not Modified 状态码,告知客户端使用本地缓存。
  • If-Modified-Since: 也是用于条件请求,客户端可以在请求中发送之前获取到的资源的最后修改时间,服务器会根据这个值判断资源是否有更新,如果没有更新,服务器会返回 304 Not Modified 状态码,告知客户端使用本地缓存。
  • Cache-Control: 用于控制缓存的行为,例如指示客户端是否可以缓存响应、缓存的有效期等。
  • Pragma: 虽然已经被 Cache-Control 取代,但仍然有一些旧的客户端和服务器会使用该头部来控制缓存行为。

for in for of的区别

for in

适用于遍历对象的可枚举属性

const obj = { a: 1, b: 2, c: 3 };

for (let key in obj) {
  console.log(key); // 输出 a, b, c
  console.log(obj[key]); // 输出 1, 2, 3
}

for of

适用于遍历可迭代对象的元素,例如数组、字符串、Map、Set 等。

const arr = [1, 2, 3];

for (let value of arr) {
  console.log(value); // 输出 1, 2, 3
}

instanceof和typeof的区别

typeof

  • typeof 是一个一元操作符,通常用于检查给定变量的数据类型
  • 返回一个表示变量类型的字符串,可能的结果包括 "undefined""boolean""number""string""object""function" 和 "symbol",以及 ES6 新增的 "bigint"
typeof 42; // 返回 "number"
typeof "hello"; // 返回 "string"
typeof true; // 返回 "boolean"
typeof undefined; // 返回 "undefined"
typeof null; // 返回 "object",这是 typeof 的一个潜在的缺陷
typeof {}; // 返回 "object"
typeof []; // 返回 "object"
typeof function(){}; // 返回 "function"

instanceof

  • instanceof 是用于**检查对象是否属于某个类(构造函数)**的二元操作符。
  • 如果对象是指定类(构造函数)或其子类的实例,则返回 true,否则返回 false
class Animal {}
class Dog extends Animal {}

const animal = new Animal();
const dog = new Dog();

console.log(animal instanceof Animal); // 返回 true
console.log(dog instanceof Dog); // 返回 true
console.log(dog instanceof Animal); // 返回 true,因为 Dog 是 Animal 的子类