前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >前端工程师自检清单73答

前端工程师自检清单73答

作者头像
小皮咖
发布2020-10-15 15:10:22
1.9K0
发布2020-10-15 15:10:22
举报
文章被收录于专栏:小皮咖

开篇

本文参考文章《一名【合格】前端工程师的自检清单》, 并对其中的部分题目进行了解答,若有遗漏或错误之处望大家指出纠正,共同进步。(点击题目展开答案!)

此文章 Markdown 源文件地址:github.com/zxpsuper/bl…

一、JavaScript基础

前端工程师吃饭的家伙,深度、广度一样都不能差。

变量和类型

1. JavaScript 规定了几种语言类型?

JavaScript中的每一个值都有它自己的类型,JavaScript规定了七种语言类型,他们是:

Undefined Null Boolean String Number Symbol Object

2. JavaScript 对象的底层数据结构是什么?

对象数据被存储于堆中 (如对象、数组、函数等,它们是通过拷贝和new出来的)。

引用类型的数据的地址指针是存储于栈中的,当我们想要访问引用类型的值的时候,需要先从栈中获得对象的地址指针,然后,在通过地址指针找到堆中的所需要的数据。

3. Symbol 类型在实际开发中的应用、可手动实现一个简单的 Symbol?

ES6 引入了一种新的原始数据类型 Symbol,表示独一无二的值。

symbol类型的 key 不能被 Object.keysfor..of 循环枚举。因此可当作私有变量使用。

代码语言:javascript
复制
let mySymbol = Symbol('key');
// 第一种写法
let a = {};
a[mySymbol] = 'Hello!';
// 第二种写法
let a = {
  [mySymbol]: 'Hello!'
};

4. JavaScript 中的变量在内存中的具体存储形式

JavaScript 中的变量分为基本类型和引用类型:

基本类型: 保存在栈内存中的简单数据段,它们的值都有固定的大小,保存在栈空间,通过按值访问

引用类型: 保存在堆内存中的对象,值大小不固定,栈内存中存放的该对象的访问地址指向堆内存中的对象,JavaScript 不允许直接访问堆内存中的位置,因此操作对象时,实际操作对象的引用

5. 基本类型对应的内置对象,以及他们之间的装箱拆箱操作

String(), Number(), Boolean()

装箱:就是把基本类型转变为对应的对象。装箱分为隐式和显示

代码语言:javascript
复制
  // 隐式装箱: 每当读取一个基本类型的值时,后台会创建一个该基本类型所对应的对象。
  // 在这个基本类型上调用方法,其实是在这个基本类型对象上调用方法。
  // 这个基本类型的对象是临时的,它只存在于方法调用那一行代码执行的瞬间,执行方法后立刻被销毁。
  let num=123;
  num.toFixed(2); // '123.00'//上方代码在后台的真正步骤为
  var c = new Number(123);
  c.toFixed(2);
  c = null;
  // 显式装箱: 通过内置对象 Boolean、Object、String 等可以对基本类型进行显示装箱。
  var obj = new String('123');

拆箱: 拆箱与装箱相反,把对象转变为基本类型的值。

代码语言:javascript
复制
  Number([1]); //1
  // 转换演变:
  [1].valueOf(); // [1];
  [1].toString(); // '1';Number('1'); //1 

6. 理解值类型和引用类型

JavaScript中的变量分为基本类型和引用类型:

基本类型: 保存在栈内存中的简单数据段,它们的值都有固定的大小,保存在栈空间,通过按值访问

引用类型: 保存在堆内存中的对象,值大小不固定,栈内存中存放的该对象的访问地址指向堆内存中的对象,JavaScript 不允许直接访问堆内存中的位置,因此操作对象时,实际操作对象的引用

7. null 和 undefined 的区别

  1. Number 转换的值不同,Number(null) 输出为 0, Number(undefined) 输出为 NaN
  2. null 表示一个值被定义了,但是这个值是空值
  3. undefined 表示缺少值,即此处应该有值,但是还没有定义

8. 至少可以说出三种判断 JavaScript 数据类型的方式,以及他们的优缺点,如何准确的判断数组类型

  1. typeof —— 返回给定变量的数据类型,可能返回如下字符串:
代码语言:javascript
复制
  'undefined'——Undefined
  'boolean'——Boolean
  'string'——String
  'number'——Number
  'symbol'——Symbol
  'object'——Object / Null (Null 为空对象的引用)
  'function'——Function
  // 对于一些如 error() date() array()无法判断,都是显示object类型
  1. instanceof 检测 constructor.prototype 是否存在于参数 object 的原型链上,是则返回 true,不是则返回 false
代码语言:javascript
复制
  alert([1,2,3] instanceof Array) // true
  alert(new Date() instanceof Date) // true
  alert(function(){this.name="22";} instanceof Function) //true
  alert(function(){this.name="22";} instanceof function) //false
  // instanceof 只能用来判断两个对象是否属于实例关系,而不能判断一个对象实例具体属于哪种类型。
复制代码
  1. constructor —— 返回对象对应的构造函数。
代码语言:javascript
复制
  alert({}.constructor === Object);  =>  true
  alert([].constructor === Array);  =>  true
  alert('abcde'.constructor === String);  =>  true
  alert((1).constructor === Number);  =>  true
  alert(true.constructor === Boolean);  =>  true
  alert(false.constructor === Boolean);  =>  true
  alert(function s(){}.constructor === Function);  =>  true
  alert(new Date().constructor === Date);  =>  true
  alert(new Array().constructor === Array);  =>  true
  alert(new Error().constructor === Error);  =>  true
  alert(document.constructor === HTMLDocument);  =>  true
  alert(window.constructor === Window);  =>  true
  alert(Symbol().constructor);    =>    undefined 
  // null 和 undefined 是无效的对象,没有 constructor,因此无法通过这种方式来判断。
复制代码
  1. Object.prototype.toString() 默认返回当前对象的 [[Class]] 。这是一个内部属性,其格式为 [object Xxx] ,是一个字符串,其中 Xxx 就是对象的类型。
代码语言:javascript
复制
  Object.prototype.toString.call(new Date);//[object Date]
  Object.prototype.toString.call(new String);//[object String]
  Object.prototype.toString.call(Math);//[object Math]
  Object.prototype.toString.call(undefined);//[object Undefined]
  Object.prototype.toString.call(null);//[object Null]
  Object.prototype.toString.call('') ;   // [object String]
  Object.prototype.toString.call(123) ;    // [object Number]
  Object.prototype.toString.call(true) ; // [object Boolean]
  Object.prototype.toString.call(Symbol()); //[object Symbol]
  Object.prototype.toString.call(new Function()) ; // [object Function]
  Object.prototype.toString.call(new Date()) ; // [object Date]
  Object.prototype.toString.call([]) ; // [object Array]
  Object.prototype.toString.call(new RegExp()) ; // [object RegExp]
  Object.prototype.toString.call(new Error()) ; // [object Error]
  Object.prototype.toString.call(document) ; // [object HTMLDocument]
  Object.prototype.toString.call(window) ; //[object global] window 是全局对象 global 的引用
  // 比较全面

9. 可能发生隐式类型转换的场景以及转换原则,应如何避免或巧妙应用

隐式转换一般说的是 Boolean 的转换

if 语句中,null""undefinded, 0, false 都会被转化为 false

一般应用于对接口数据判空时使用

10. 出现小数精度丢失的原因,JavaScript 可以存储的最大数字、最大安全数字,JavaScript处理大数字的方法、避免精度丢失的方法

  • 精度丢失原因,说是 JavaScript 使用了 IEEE 754 规范,二进制储存十进制的小数时不能完整的表示小数
  • 能够表示的最大数字 Number.MAX_VALUE 等于 1.7976931348623157e+308 ,最大安全数字 Number.MAX_SAFE_INTEGER 等于 9007199254740991
  • 避免精度丢失
    • 计算小数时,先乘 1001000,变成整数再运算
    • 如果值超出了安全整数,有一个最新提案,BigInt 大整数,它可以表示任意大小的整数,注意只能表示整数,而不受安全整数的限制

原型和原型链

1. 理解原型设计模式以及 JavaScript 中的原型规则

代码语言:javascript
复制
A. 所有的引用类型(数组、对象、函数),都具有对象特性,即可自由扩展属性;
B. 所有的引用类型(数组、对象、函数),都有一个`__proto__`属性(隐式原型),属性值是一个普通的对象;
C. 所有的函数,都具有一个 `prototype`(显式原型),属性值也是一个普通对象;
D. 所有的引用类型(数组、对象、函数),其隐式原型指向其构造函数的显式原型;`(obj._proto_ === Object.prototype)`;
E. 当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么会去它的 `__proto__` (即它的构造函数的 `prototype`)中去寻找;

2. instanceof 的底层实现原理,手动实现一个 instanceof

简单说就是判断实例对象的__proto__是不是强等于对象的prototype属性,如果不是继续往原型链上找,直到 __proto__null 为止。

代码语言:javascript
复制
function instanceOf(obj, object) {//obj 表示实例对象,object 表示对象
  var O = object.prototype;
  obj = obj.__proto__;
  while (true) { 
      if (obj === null) 
          return false; 
      if (O === obj) // 这里重点:当 O 严格等于 obj 时,返回 true 
          return true; 
      obj = obj.__proto__; 
  } 
}
复制代码

3. 理解 JavaScript 的执行上下文栈,可以应用堆栈信息快速定位问题

执行上下文 就是当前 JavaScript 代码被解析和执行时所在环境的抽象概念, JavaScript 中运行任何的代码都是在执行上下文中运行。

执行上下文总共有三种类型:全局执行上下文, 函数执行上下文, Eval 函数执行上下文

执行栈,在其他编程语言中也被叫做调用栈,具有 LIFO(后进先出)结构,用于存储在代码执行期间创建的所有执行上下文。

4. 实现继承的几种方式以及他们的优缺点

详情请点击:《继承的几种实现方式》

5. 可以描述 new 一个对象的详细过程,手动实现一个 new 操作符

  • new一个对象的详细过程:
代码语言:javascript
复制
function Test() {}
const test = new Test();
复制代码
  1. 创建一个对象 const obj = {}
  2. 设置新对象的 constructor 属性为构造函数的名称,设置新对象的__proto__属性指向构造函数的 prototype 对象
代码语言:javascript
复制
obj.constructor = Test;
obj.__proto__ = Test.prototype;
  1. 使用新对象调用函数,函数中的 this 被指向新实例对象 Test.call(obj)
  2. 将初始化完毕的新对象地址,保存到等号左边的变量中
  • 实现一个new操作符
代码语言:javascript
复制
function myNew(Obj,...args){
    var obj = Object.create(Obj.prototype);//使用指定的原型对象及其属性去创建一个新的对象
    Obj.apply(obj,args); // 绑定 this 到obj, 设置 obj 的属性
    return obj; // 返回实例
}
复制代码

6. 理解 es6 class 构造以及继承的底层实现原理

  • ES6 类的底层还是通过构造函数去创建的。
代码语言:javascript
复制
// es6 Parent类实现
class Parent {
  constructor(name,age){
      this.name = name;
      this.age = age;
  }
  speakSomething(){
      console.log("I can speek chinese");
  }
}
// 转化为
var Parent = function () {
  function Parent(name, age) {
      _classCallCheck(this, Parent); // 判断实例 Parent instanceof Parent(函数)是否为true

      this.name = name;
      this.age = age;
  }
  // 此方法通过使用 Object.defineProperty 为 function Parent 的 prototype 添加属性值
  _createClass(Parent, [{
      key: "speakSomething",
      value: function speakSomething() {
          console.log("I can speek chinese");
      }
  }]);

  return Parent;
}();
复制代码
  • ES6 的继承实现
代码语言:javascript
复制
//定义子类,继承父类
class Child extends Parent {
  static width = 18
  constructor(name,age){
      super(name,age);
  }
  coding(){
      console.log("I can code JS");
  }
}
// 转化为
var Child = function (_Parent) {
  _inherits(Child, _Parent);

  function Child(name, age) {
      _classCallCheck(this, Child);

      return _possibleConstructorReturn(this, (Child.__proto__ || Object.getPrototypeOf(Child)).call(this, name, age));
  }

  _createClass(Child, [{
      key: "coding",
      value: function coding() {
          console.log("I can code JS");
      }
  }]);

  return Child;
}(Parent);
复制代码

这里其实就是多了一个 _inherits(Child, _Parent); 方法,实现了以下功能,具体可看文章《ES6类以及继承的实现原理》

代码语言:javascript
复制
  //实现的结果是:
  subClass.prototype.__proto__ = superClass.prototype
  subClass.__proto__ = superClass // 实现静态属性的继承

作用域和闭包

1. 理解词法作用域和动态作用域

词法作用域也称静态作用域,javascript 采用静态作用域

静态作用域 —— 函数的作用域基于函数创建的位置。

动态作用域 —— 函数的作用域基于函数的使用位置。

代码语言:javascript
复制
var value = 1;

function foo() {
  console.log(value);
}

function bar() {
  var value = 2;
  foo();
}

bar(); // 输出 1 。JavaScript 采用的是词法作用域,也称为静态作用域。相同的,动态作用域此代码应该输出 2
复制代码

2. 理解 JavaScript 的作用域和作用域链

作用域(scope)就是变量访问规则的有效范围。

JavaScript 中全局变量的作用域是全局的,在代码的任何地方都是有定义的。然而函数的参数和局部变量只在函数体内有定义。另外局部变量的优先级要高于同名的全局变量,也就是说当局部变量与全局变量重名时,局部变量会覆盖全局变量。

3. this的原理以及几种不同使用场景的取值

this的几种不同使用场景的取值 + JavaScript 的 this 原理

4. 闭包的实现原理和作用,可以列举几个开发中闭包的实际应用

原理:闭包就是能够读取其他函数内部变量的函数。由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成"定义在一个函数内部的函数"。

所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。

作用:闭包可以用在许多地方。它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。

应用:1. 匿名自执行函数 2. 结果缓存 3. 封装局部变量

参考链接:《学习Javascript闭包(Closure)》

5. 理解堆栈溢出和内存泄漏的原理,如何防止

堆栈溢出 的产生是由于过多的函数调用,导致调用堆栈无法容纳这些调用的返回地址,一般在递归中产生。堆栈溢出很可能由无限递归(Infinite recursion)产生,但也可能仅仅是过多的堆栈层级.

参考链接:《内存泄漏与避免》

6. 如何处理循环的异步操作

  1. 将异步操作变同步,使用 async/await.
  2. 去掉循环,将循环变成递归

执行机制

1. 为何 try 里面放 return,finally 还会执行,理解其内部机制

try 语句中,在执行 return 语句时,要返回的结果已经准备好了,就在此时,程序转到 finally 执行了。

在转去之前,try 中先把要返回的结果存放到局部变量中去,执行完 finally 之后,在从中取出返回结果。

因此,即使finally 中对返回的结果进行了改变,但是不会影响返回结果。

它应该使用栈保存返回值。

2. JavaScript 如何实现异步编程,可以详细描述 EventLoop 机制

JavaScript 如何实现异步编程:

  1. callback (回调函数) 回调函数代表着,当某个任务处理完,然后需要做的事。比如读取文件,连接数据库,等文件准备好,或数据库连接成功执行编写的回调函数,又比如像一些动画处理,当动画走完,然后执行回调。
  2. 发布订阅模式 顾名思义,便是先订阅了事件,有人一发布事件你就知道了,接着执行后面的操作。
  3. Promise Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件的结果,相比回调函数,Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
  4. Generator (生成器)函数 Generator 函数是 ES6 提供的一种异步编程解决方案,其行为类似于状态机。
  5. async/await async/await 本质上还是基于 Generator 函数,可以说是 Generator 函数的语法糖,async 就相当于之前写的run函数(执行Generator函数的函数),而 await 就相当于 yield ,只不过 await 表达式后面只能跟着 Promise 对象,如果不是 Promise 对象的话,会通过 Promise.resolve 方法使之变成 Promise 对象。async 修饰 function,其返回一个 Promise 对象。

《浏览器 Event Loop 机制》

3. 宏任务和微任务分别有哪些

宏任务: setTimeout,setInterval,setImmediate (Node独有),requestAnimationFrame (浏览器独有),I/O,UI rendering (浏览器独有)

微任务: process.nextTick (Node独有),Promise,Object.observe,MutationObserver

4. 可以快速分析一个复杂的异步嵌套逻辑,并掌握分析方法

代码语言:javascript
复制
// 执行顺序,先微队列,后宏队列。
console.log(1);
setTimeout(() => {
  console.log(2);
  setTimeout(() => {
    console.log(8);
  })
  Promise.resolve().then(() => {
    console.log(3)
  });
});
new Promise((resolve, reject) => {
  console.log(4)
  setTimeout(() => {
    console.log(10);
  })
  resolve()
}).then(() => {
  console.log(5);
  Promise.resolve().then(() => {
    console.log(11)
  });
  setTimeout(() => {
    console.log(13);
  })
})
setTimeout(() => {
  Promise.resolve().then(() => {
    console.log(9)
  });
  console.log(6);
  setTimeout(() => {
    console.log(12);
  })
})
console.log(7);
复制代码

从头至尾执行一次代码,根据上面分类规则分至不同队列, new promise( function ) 也是立即执行。setTimeout 的回调函数属于宏队列(macrotask)resolve 的回调函数属于微队列

代码语言:javascript
复制
// 栈区(stack)
console.log(1);
console.log(4);
console.log(7);
代码语言:javascript
复制
// 宏队列
() => {
  console.log(2);
  setTimeout(() => {
    console.log(8);
  })
  Promise.resolve().then(() => {
    console.log(3)
  });
}
() => {
  console.log(10);
}
() => {
  Promise.resolve().then(() => {
    console.log(9)
  });
  console.log(6);
  setTimeout(() => {
    console.log(12);
  })
}
复制代码
代码语言:javascript
复制
// 微队列
() => {
  console.log(5);
  Promise.resolve().then(() => {
    console.log(11)
  });
  setTimeout(() => {
    console.log(13);
  })
}
复制代码

优先执行微队列,微队列执行过程中产生的微队列和宏队列置于队列末尾排序执行,而宏队列产生的微队列和宏队列于新的队列中等待。。

执行微队列:(分类)

代码语言:javascript
复制
// 栈区(stack)
console.log(1);
console.log(4);
console.log(7);
//////////
console.log(5);
代码语言:javascript
复制
// 微队列
() => {
  console.log(11)
});
代码语言:javascript
复制
// 宏队列
() => {
  console.log(2);
  setTimeout(() => {
    console.log(8);
  })
  Promise.resolve().then(() => {
    console.log(3)
  });
}
() => {
  console.log(10);
}
() => {
  Promise.resolve().then(() => {
    console.log(9)
  });
  console.log(6);
  setTimeout(() => {
    console.log(12);
  })
}
() => {
    console.log(13);
}
复制代码

此时新增了一个微队列console.log(11),因为是微队列产生的,继续执行:

代码语言:javascript
复制
// 栈区(stack)
console.log(1);
console.log(4);
console.log(7);
//////////
console.log(5);
/////////
console.log(11)
代码语言:javascript
复制
// 微队列-空
代码语言:javascript
复制
// 宏队列
() => {
  console.log(2);
  setTimeout(() => {
    console.log(8);
  })
  Promise.resolve().then(() => {
    console.log(3)
  });
}
() => {
  console.log(10);
}
() => {
  Promise.resolve().then(() => {
    console.log(9)
  });
  console.log(6);
  setTimeout(() => {
    console.log(12);
  })
}
() => {
    console.log(13);
}
复制代码

执行完微队列后执行宏队列:

代码语言:javascript
复制
// 栈区(stack)
console.log(1);
console.log(4);
console.log(7);
//////////
console.log(5);
/////////
console.log(11);
/////////
console.log(2);
console.log(10);
console.log(6);
console.log(13);
代码语言:javascript
复制
// 微队列
() => {
  console.log(3)
}
() => {
  console.log(9)
}
代码语言:javascript
复制
// 宏队列
() => {
  console.log(8);
}
() => {
  console.log(12);
}

接下来执行微队列后宏队列,即:

代码语言:javascript
复制
// 栈区(stack)
console.log(1);
console.log(4);
console.log(7);
//////////
console.log(5);
/////////
console.log(11);
/////////
console.log(2);
console.log(10);
console.log(6);
console.log(13);
////////
console.log(3)
console.log(9)
////////
console.log(8);
console.log(12);

5. 使用 Promise 实现串行

代码语言:javascript
复制
// 一个 promise 的 function
function delay(time) {
 return new Promise((resolve, reject) => {
   console.log(`wait ${time}s`)
   setTimeout(() => {
     console.log('execute');
     resolve()
   }, time * 1000)
 })
}
const arr = [3, 4, 5];
复制代码
  1. reduce
代码语言:javascript
复制
arr.reduce((s, v) => {
 return s.then(() => delay(v))
}, Promise.resolve())
复制代码
  1. async + 循环 + await
代码语言:javascript
复制
(
 async function () {
   for (const v of arr) {
     await delay(v)
   }
 }
)()
复制代码
  1. 普通循环
代码语言:javascript
复制
let p = Promise.resolve()
for (const i of arr) {
 p = p.then(() => delay(i))
}
复制代码
  1. 递归
代码语言:javascript
复制
function dispatch(i, p = Promise.resolve()) {
 if (!arr[i]) return Promise.resolve()
 return p.then(() => dispatch(i + 1, delay(arr[i])))
}
dispatch(0)
复制代码

6. Node 与浏览器 EventLoop 的差异

《JavaScript 运行机制详解:再谈Event Loop》

《带你彻底弄懂Event Loop》

7. 如何解决页面加载海量数据而页面不卡顿

  1. 分治思想,在一定的时间内多次加载数据,直至渲染完成,使用 window.requestAnimationFramedocument.createDocumentFragment() 实现, 可参考文章【如何解决页面加载海量数据而不冻结前端UI】
  2. 局部显示,毕竟用户能看到的就一屏内容,监听用户的滚动行为,改变显示元素,可使 DOM 结构最简单化。可参考文章【大数据如何在前端流畅展示】,不过他的 Demo有点问题.

语法和API

1. 理解 ECMAScript 和 JavaScript 的关系

ECMAScriptJavaScript 的规范,JavaScriptECMAScript 的实现。

2. 熟练运用 es5、es6 提供的语法规范

【JavaScript 标准参考教程(alpha)】

【ECMAScript 6 入门】

3. setInterval 需要注意的点,使用 settimeout 实现 setInterval

  • setInterval 需要注意的点:

在使用 setInterval 方法时,每一次启动都需要对 setInterval 方法返回的值做一个判断,判断是否是空值,若不是空值,则要停止定时器并将值设为空,再重新启动,如果不进行判断并赋值,有可能会造成计时器循环调用,在同等的时间内同时执行调用的代码,并会随着代码的运行时间增加而增加,导致功能无法实现,甚至占用过多资源而卡死奔溃。因此在每一次使用setInterval方法时,都需要进行一次判断。

代码语言:javascript
复制
let timer = setInterval(func, 1000)
// 在其他地方再次用到setInterval(func, 1000)
if (timer !== null) {
    clearInterval(timer)
    timer = null
}
timer = setInterval(func, 1000)
  • 使用 settimeout 实现 setInterval
代码语言:javascript
复制
setIntervalFunc = () =>{
  console.log(1) //使用递归
  setTimeout(setIntervalFunc, 1000);
};
setInterval()
复制代码

4. JavaScript 提供的正则表达式 API、可以使用正则表达式(邮箱校验、URL解析、去重等)解决常见问题

邮箱校验:

代码语言:javascript
复制
function isEmail(emailStr) {
    return /^[a-zA-Z0-9]+([._-]*[a-zA-Z0-9]*)*@[a-zA-Z0-9]+.[a-zA-Z0-9{2,5}$]/.test(emailStr);
}
复制代码

URL解析:

代码语言:javascript
复制
function isUrl(urlStr) {
    return /^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\*\+,;=.%]+$/.test(value)
}
复制代码

数组去重:

代码语言:javascript
复制
// set结构
let arr = [1, 1, 2, 2, 3, 3]
arr2 = [...new Set(arr)]
console.log(arr2) // [1,2,3]

// Object.keys(), 利用属性 key 的唯一性
let arrObj = [1, 1, 2, 2, 3, 3]
arrObj2 = {}
for (i in arrObj) {
    arrObj2[arrObj[i]] = true
}
let arrObj3 = Object.keys(arrObj2)
console.log(arrObj3)

// 利用 indexOf() 查询数组内是否已经包含该元素
var arrIndexOf = ['a','c','b','d','a','b']
var arrIndexOf2 = [];
for(var i = 0;iif(arrIndexOf2.indexOf(arrIndexOf[i])<0){
        arrIndexOf2.push(arrIndexOf[i]);
    }
}
console.log(arrIndexOf2)// ['a', 'c', 'b', 'd']

二、HTML和CSS

HTML

1. 从规范的角度理解 HTML,从分类和语义的角度使用标签

语义化标签

  • 让页面呈现清晰的结构
  • 屏幕阅读器(如果访客有视障)会完全根据你的标记来“读”你的网页
  • 搜索引擎的爬虫依赖标签确定上下文和权重问题
  • 便于团队开发和维护
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 开篇
  • 一、JavaScript基础
    • 变量和类型
      • 原型和原型链
        • 作用域和闭包
          • 执行机制
            • 语法和API
            • 二、HTML和CSS
              • HTML
              相关产品与服务
              数据库
              云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档