2021前端笔试题
时间:2023-02-06 19:00:00
笔试重点:查漏补缺
JavaScript题1: https://github.com/lydiahallie/javascript-questions/blob/master/zh-CN/README-zh_CN.md
JavaScript题2: https://blog.csdn.net/liuyan19891230/article/details/102385743
(一)
1.什么是闭包,为什么要用闭包?
-
嵌套在一个函数中的另一个函数是一个封闭的包。
-
功能:创建私有变量,减少全局变量,防止变量名污染。可操作外部功能域的变量,变量不会被浏览器回收,变量值。
2.以下代码输出结果是什么?想输出吗? 0 1 2 3 4 怎么解决?
for(var i = 0; i < 5; i ){ setTimeout(function(){ console.log(i); },1000) }
答案: 输出结果为 5个5
输出 0 1 2 3 4 5 方法如下:
方法一:
for (let i = 0; i < 5; i ) { setTimeout(function() { console.log(i); }, 1000); }
方法二:
for (var i = 0; i < 5; i ) { (function(j) { setTimeout(function() { console.log(j); }, 1000); })(i); }
方法三:
for (var i = 0; i < 5; i ) { setTimeout(function(j) { console.log(j); }, 1000, i); }
3.委托分析事件?
事件委托是指将事件绑定到目标元素的父元素上,利用泡沫机制触发事件。
ulEl.addEventListener('click', function(e){ var target = event.target || event.srcElement; if(!!target && target.nodeName.toUpperCase() === "LI"){ console.log(target.innerHTML); } }, false);
4.有以下段落HTML写一段代码 js 实现:单击列表时,输出相应的索引。
- 1
-
- 2
-
- 3
-
- 4
-
方法1:立即使用执行函数
var liList = document.getElementsByTagName('li') for (var i = 0; i < liList.length; i ) { (function(i){ liList[i].onclick = function(){ console.log(i) } })(i) };
方法二:使用ES6的let
var liList = document.getElementsByTagName('li') for (let i = 0; i < liList.length; i ) { liList[i].onclick = function(){ console.log(i) } };
方法三:forEach实现
var liList = document.getElementsByTagName('li') var liArr = [].slice.call(liList)//将liList这类数组转化为数组,forEach只能遍历数组 liArr.forEach(function(ele,i){ ele.onclick = function(){ console.log(i) } })
方法四:call方法
var liList = document.getElementsByTagName('li') function fun(idx){ this.onclick = function(){ console.log(idx) } } for (var i = 0; i < liList.length; i ) { fun.call(liList[i],i) };
5、数组方法 pop() push() unshift() shift() 的作用
shift():删除数组的第一个元素,返回第一个元素的值。
pop() : 删除数组的最后一个元素,返回最后一个元素的值。
push():在数组末尾添加一个或多个元素,以返回新数组的长度。
unshift() :将一个或多个元素添加到数组头部,以返回新数组的长度。
6、var arr = [4,0,7,9,0,0,2,6,0,3,1,0];
数组中的要求 0 去掉项目,不会 0 将值存储在新的数组中,生成新的数组。
答案:
方法1:逐步反映要求
//定义数组 var arr = [4, 0, 7, 9, 0, 0, 2, 6, 0, 3, 1, 0]; //创建空数组,接收新生成的数组 var mn=[] //创建函数 function fn(arr,ma) { ///循环添加随机元素 for(var i=0;i
方法二:使用 forEach
var arr = [4,0,7,9,0,0,2,6,0,3,1,0]; var a = [] arr.forEach(item=>{ if(item != 0){ a.push(item) } }) console.log('a',a)
方法三:使用 for 循环
var arr = [4,0,7,9,0,0,2,6,0,3,1,0]; var b = [] for(var i = 0; i < arr.length; i ){ if(arr[i] != 0){ b.push(arr[i]) } } console.log('b',b)
7、如何解决 ajax 地狱回调问题?
Promise使用场景:ajax请求、回调函数、复杂操作判断。 Promise是ES为了解决异步编程的诞生。
异步操作解决方案:Promise、Generator、定时器(不知道算不算)ES7的async
8、谈谈你对 ES6 的理解?
- 新模板字符串(为 JavaScript 字符串插值功能简单)
- 箭头函数
- for-of(用于遍历数据-例如数组中的值。
- arguments 不确定参数和默认参数可以完美地替代对象。
- ES6 将 promise 对象纳入规范,提供原生 Promise 对象。
- 增加了 let 和 const 用于声明变量的命令。
- 块级作用域增加。
- let 实际上,命令增加了块级作用域。
- 还有介绍 module 模块的概念
9.常用的库有哪些?开发了哪些应用或组件?
10、用一个 div 模拟 textarea 的实现
给 div 添加 contenteditable=true
即可。
步骤:
- 给 div 添加一个HTML全局属性:
contenteditable="true"
,使 div 用户可以编辑元素; - 给 div 添加样式
resize: vertical;
,使 div 用户可以调整尺寸。注:别忘了设置 overflow:auto; 样式,因为 resize 款式不适合 verflow:visible; 的块,不然 resize 不起效; - 增加一个属性:placeholder=“请输入……”;
- 通过 CSS 选择器获取并显示 placeholder 的值;
代码实现:
.textarea{
height: 200px;
width: 300px;
padding: 4px;
border: 1px solid #888888;
resize: vertical;
overflow: auto;
}
.textarea:empty:before {
content: attr(aria-placeholder);
color: #bbb;
}
10-1、CSS限制显示字数,文字长度超出部分用省略号表示
(1) 文字超出一行,省略超出部分,显示’…’
.line-limit-length {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap; /*文本不换行,这样超出一行的部分被截取,显示...*/
}
(2) 可以给定容器宽度限制,超出部分省略。
.product-buyer-name {
max-width: 110px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
11、怎么让 Chrome 支持小于 12px 的文字?
谷歌 Chrome 最小字体是 12px,不管你设置成 8px 还是 10px,在浏览器中只会显示 12px
针对谷歌浏览器内核,加 webkit 前缀,用 transform:scale()
这个属性进行缩放。
测试10px
最小12px
12、CSS垂直局居中的写法,请至少写出 2 种。
(1)已知容器尺寸
方法一:利用定位(常用方法,比较推荐)
方法二:利用 margin:auto;
方法三:利用 display:table-cell
方法四:利用 display:flex;设置垂直水平都居中
(2)未知容器尺寸
方法一:
div {
position: relative; /* 相对定位或绝对定位均可 */
width:500px;
height:300px;
top: 50%;
left: 50%;
margin: -150px 0 0 -250px; /* 外边距为自身宽高的一半 */
background-color: pink; /* 方便看效果 */
}
方法二:
未知容器的宽高,利用 transform
属性
div {
position: absolute; /* 相对定位或绝对定位均可 */
width:500px;
height:300px;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: pink; /* 方便看效果 */
}
方法三:
利用 flex 布局
实际使用时应考虑兼容性
.container {
display: flex;
align-items: center; /* 垂直居中 */
justify-content: center; /* 水平居中 */
}
.container div {
width: 100px;
height: 100px;
background-color: pink; /* 方便看效果 */
}
13、CSS实现宽度自适应 100%,宽高 16:9 的比例的矩形。
解释:通过百分比设置宽度,根据宽度的比例数值*9/16,计算出高度的百分比数值,设置内边距为高度的数值,最后用绝对定位 百分百填充 item。
做法:
第一步先计算高度,假设宽 100%,那么高为h=9/16=56.25%
第二步利用之前所说设置 padding-bottom 方法实现矩形
16:9的矩形
14、 [] == []
输出的结果是?为什么?
- []单独用在==表达式中时,会隐含转型为""(空字符串)
- 一个不是 null 或者 undefined 的对象(包括[])用在布尔表达式中,会被作为 true 处理。因此 [] 结果是 true,![] 的结果是false。
- 所以:
[]==[]
等价于""==true
[]==![]
等价于 ""==false
,
- 而空字符串在布尔表达式中作为 false 处理。
- 因此:
[] == []
结果是 false
[] == ![]
结果是 true
15、JavaScript 的基本数据类型都有什么?
- 基本数据类型:Number、String、Boolean、undefined、Null、Symbol、ES10新增的BigInt(任意精度整数)七类。
- 引用数据类型(Object类):常规名值对的无序对象 {a:1},数组[1,2,3],以及函数。
原始数据类型:
- 布尔类型:布尔表示一个逻辑实体,可以有两个值:true 和 false。
- Null 类型:Null 类型只有一个值: null。
- Undefined 类型:一个没有被赋值的变量会有个默认值 undefined。
- 数字类型:根据 ECMAScript 标准,JavaScript 中只有一种数字类型:基于 IEEE 754 标准的双精度 64 位二进制格式的值(-(253 -1) 到 253
-1)。它并没有为整数给出一种特定的类型。除了能够表示浮点数外,还有一些带符号的值:+Infinity,-Infinity 和 NaN (非数值,Not-a-Number)。
- BigInt 类型:BigInt类型是 JavaScript 中的一个基础的数值类型,可以用任意精度表示整数。使用 BigInt,您可以安全地存储和操作大整数,甚至可以超过数字的安全整数限制。BigInt是通过在整数末尾附加 n 或调用构造函数来创建的。
- String字符串类型:JavaScript的字符串类型用于表示文本数据。它是一组16位的无符号整数值的“元素”。在字符串中的每个元素占据了字符串的位置。第一个元素的索引为0,下一个是索引1,依此类推。字符串的长度是它的元素的数量。
- Symbols符号类型:符号(Symbols)是ECMAScript 第6版新定义的。符号类型是唯一的并且是不可修改的, 并且也可以用来作为Object的key的值(如下). 在某些语言当中也有类似的原子类型(Atoms).
你也可以认为为它们是C里面的枚举类型。
引用数据类型:
- Object对象: 在计算机科学中, 对象是指内存中的可以被 标识符引用的一块区域。
16、实现一个对象的浅拷贝和一个基本的深拷贝(可以不考虑函数)
浅拷贝:
demo数据:
// 对象的浅拷贝
var obj = {
name: '张三',
age: 33,
sex: '男',
hobby: {
a: '1'
}
}
方法一:使用循环遍历对象,将其键值赋值给一个新数组;
var newObj = {};
// 循环遍历数组
for (var k in obj) {
newObj[k] = obj[k];
}
console.log(newObj); //{name: "张三", age: 33, sex: "男", hobby: {…}}
newObj.hobby.a = '2';
console.log(obj.hobby.a); // 2
console.log(newObj.hobby.a); // 2
方法二:采用 Object.assign(新数组,拷贝的数组); 方法
var newObj = {};
Object.assign(newObj, obj);
console.log(newObj); // {name: "张三", age: 33, sex: "男", hobby: {…}}
newObj.hobby.a = '2';
console.log(obj.hobby.a); //2
console.log(newObj.hobby.a); //2
深拷贝:
demo数据格式:
// demo test
var objTest = {
name:"gino",
sex:"male",
family:{
brother:"zhipeng",
mother:"SunAiyun",
}
};
var arrTest = ["a","b","c","d"];
方法一:JSON.parse(JSON.stringify(arrTest))
不能拷贝对象
方法二:
//深拷贝
function deepCopy(arr) {
let copyArr = arr.constructor === Array ? [] : {}
for (let i in arr) {
if (typeof arr[i] === 'object') {
copyArr[i] = deepCopy(arr[i]) // 引用类型的话进行递归操作
} else {
copyArr[i] = arr[i] // 值类型直接赋值
}
}
return copyArr
}
方法三:使用递归:
function deepClone_1(obj){
let objClone = Array.isArray(obj)?[]:{};
if(obj && typeof obj==="object"){
for(key in obj){
if(obj.hasOwnProperty(key)){
//判断ojb子元素是否为对象,如果是,递归复制
if(obj[key]&&typeof obj[key] ==="object"){
objClone[key] = deepClone_1(obj[key]);
}else{
//如果不是,简单复制
objClone[key] = obj[key];
}
}
}
}
return objClone;
}
console.log('对象深拷贝', deepClone_1(objTest))
console.log('数组深拷贝', deepClone_1(arrTest))
方法四:利用数组的 Array.prototype.forEach 和其他一些 ECMAScript 5中的 Object.* 函数
var deepClone_2 = function (obj){
var copy = Object.create( Object.getPrototypeOf(obj) );
var propNames = Object.getOwnPropertyNames(obj);
propNames.forEach(function(name){
if(typeof obj[name] === 'object') {
let copyObj = deepClone_2(obj[name])
copy[name] = copyObj
} else {
var desc = Object.getOwnPropertyDescriptor(obj, name);
Object.defineProperty(copy, name, desc);
}
});
return copy;
}
console.log('对象深拷贝', deepClone_2(objTest))
console.log('数组深拷贝', deepClone_2(arrTest))
方法五:
// 对象的浅拷贝
var obj = {
name: '张三',
age: 33,
sex: '男',
hobby: {
a: '1'
}
}
var newObj = {};
//递归循环
function deepCopy_3(newObj, oldObj) {
for (var k in oldObj) {
if (oldObj[k] instanceof Array) {
newObj[k] = [];
deepCopy_3(newObj[k], oldObj[k]);
}
else if (obj[k] instanceof Object) {
newObj[k] = {};
deepCopy_3(newObj[k], oldObj[k]);
} else {
// 基本数据类型
newObj[k] = oldObj[k];
}
}
}
// 对象深拷贝
deepCopy_3(newObj, obj);
console.log(newObj); // {name: "张三", age: 33, sex: "男", hobby: {…}}
newObj.hobby.a = '2';
console.log(obj.hobby.a); // 1
console.log(newObj.hobby.a); // 2
17、数据格式 JSON 转换为字符串,以及把字符串转换为 JSON 的方法。
(1)字符串 → JSON对象:
-
JSON.parse()方法
var a = ‘{“a”:2}’;
JSON.parse(a); //{a:2}
-
eval() 方法:计算某一个字符串,并执行其中的 javascript 代码 【不推荐】
var s = ‘{a:2}’;
eval(’(’ + s + ‘)’); //将json字符串转成json对象,需要在字符串外包裹一对括号(), {a:2}
(2)json对象 → 字符串
-
JSON.stringify()方法 ,将非字符串转成字符串
var s = {‘a’:2};
JSON.stringify(s); //"{“a”:2}"
(3)将值转换成字符串(值 → 字符串)
-
转换函数 toString(),弱类型转换,强制类型转换
value.toString() 将数值转成对应进制的字符串
var n =8;
n.toString(几进制);
注意:不能将 null 和 undefined 转换成字符串
- 弱类型转换:
value + ''
将值转换成字符串
2+ ''
- 强制类型转换:String(value)
(4)字符串 → 值
-
转换函数:parseInt() 和 parseFloat()
parseInt(“1234blue”); //returns 1234
parseInt(“22.5”); //returns 22
parseInt(“blue”); //returns NaN
parseInt(“AF”, 16); //returns 175
parseInt(“10”, 2); //returns 2
parseFloat(“1234blue”); //returns 1234.0
parseFloat(“22.5”); //returns 22.5
parseFloat(“blue”); //returns NaN
-
强制类型转换
Number(value)——把给定的值转换成数字(可以是整数或浮点数);
Number(false) 0
Number(true) 1
Number(undefined) NaN
Number(null) 0
Number( "5.5 ") 5.5
Number( "56 ") 56
Number( "5.6.7 ") NaN
补充:小数
-
四舍五入
var num =2.4492425542;
num = num.toFixed(2); // 输出结果为 2.45
-
不四舍五入 (不进位)
Math.floor(15.7784514000 * 100) / 100 // 输出结果为 15.77
-
当成字符串,使用正则匹配
Number(15.7784514000.toString().match(/^d+(?:.d{0,2})?/))
// 输出结果为 15.77,不能用于整数如 10 必须写为10.0000
注意:如果是负数,请先转换为正数再计算,最后转回负数。
18、说出至少 4 种 vue 当中的指令和它的用法
- v-if:判断是否隐藏
- v-for:数据循环
- v-bind:class:绑定一个属性
- v-model:实现数据双向绑定
19、vue-router 怎么定义动态路由的?怎么获取传过来的动态参数?
-
简单回答:
定义:path:'a/:value'
获取:this.$route.params.value
-
全面了解:
可以通过 query , param 两种方式
区别: query 通过 url 传参,刷新页面还在 params 刷新页面不在了。
(1)params 的类型:
配置路由格式: /router/:id
传递的方式: 在path后面跟上对应的值
传递后形成的路径: /router/123
用户
//在index.js中
{
path: ‘/user/:userid’,
component: User,
},
跳转方法:
// 方法1:
按钮
// 方法2:
this.$router.push({name:'users',params:{uname:wade}})
// 方法3:
this.$router.push('/user/' + wade)
可以通过 $route.params.userid 获取你说传递的值
(2)query 的类型
配置路由格式: /router,也就是普通配置
传递的方式: 对象中使用 query 的 key 作为传递方式
传递后形成的路径: /routeid=123
档案
// 方法二:或者写成按钮以点击事件形式
//点击事件
profileClick(){
this.$router.push({
path: "/profile",
query: {
name: "kobi",
age: "28",
height: 198
}
});
}
跳转方法:
// 方法1:
按钮
// 方法2:
this.$router.push({ name: 'users', query:{ uname:james }})
// 方法3:
按钮
// 方法4:
this.$router.push({ path: '/user', query:{ uname:james }})
// 方法5:
this.$router.push('/user?uname=' + jsmes)
可以通过 $route.query 获取你说传递的值。
20、分别写出 webpack 打包给服务器和本地开发预览的命令代码。
npm run build
和 npm run dev
21、下面代码
let a = [1,2,4]
let b = a.map(v=>{v = v* 2}) 写出 b 的值

原题中没有 return v,所以b的值是 [undefined,undefined,undefined]。
如果有 return v,则 b 的值是 [2,4,8],正常 map 写法。
22、请生成下面这段DOM结构,要求:使用标准的DOM方法或属性。
请编辑这段话!
答案:
主要用到的函数:createElement,appendChild,createTextNode,setAttribute 这几个函数。
(1)创建元素的DOM方法 – createElement;
(2)设置元素属性的DOM方法 – setAttribute;
(3)添加到元素上的DOM方法 – appendChild;
(4)创建文字的DOM方法 - createTextNode;
(5)设置样式的DOM属性 – className(HTML DOM属性)
var oDiv=document.createElement("div");
var op=document.createElement("p");
oDiv.appendChild(op);
op.className="slogan";
var oText=document.createTextNode("请编辑这段话!");
op.appendChild(oText);
oDiv.setAttribute("id","example");
document.body.appendChild(oDiv);
23、请使用 Promise 封装 Ajax 操作
原始的 Ajax 操作如下:
var onSuccess = function(result){}; //成功的回调
var onFail = function(error){}; //失败的回调
var req = new XMLHttpRequest();
req.open("POST", "www.baidu.com", true);
req.onload = function(){
if( req.readyState === 4 && req.status === 200 ){
onSuccess(req.response);
}else {
onFail(req.statusText);
}
}
req.onerror = function(){
onFail(Error("网络异常"));
}
答案1:
//参考:https://www.nowcoder.com/questionTerminal/86311e67b9124788b68acdf002da55f8
const ajax = url => {
return new Promise((resolve, reject) => {
let req = new XMLHttpRequest();
req.open("POST", url, true);
req.onload = () => {
if(req.readyState === 4 && req.status === 200){
resolve(req.response);
} else {
reject(req.statusText);
}
}
req.onerror = () => {
reject(Error("网络异常"));
}
})
}
答案2:
return new Promise(function(resolve, reject){
var req = new XMLHttpRequest();
req.open("POST", "www.baidu.com", true);
req.onload = function(){
if(req.readyState === 4 && req.status === 200){
resolve(req.response);
} else {
reject(req.statusText);
}
};
req.onerror = function(){
reject(Error("网络异常"));
}
});
答案3:
// 参考:https://blog.csdn.net/weixin_45298413/article/details/108809108
const promise = new Promise((resove,reject)=>{
var req = new XMLTttpRequest();
req.open("post","http://www.baidu.com",true);
req.onload = function(){
if(req.readyState === 4 && req.status === 200){
onSuccess(req.response);
} else {
onFail(req.statusText);
}
}
req.onerror = function(){
onFail(Error("网络异常"));
}
})
延伸:
(1)利用Promise知识,用原生JS封装Ajax
参考:https://www.jianshu.com/p/76b32c84216a
//封装一个Promise函数
function queryData (url){
//创建一个Promise实例
var p = new Promise(function(resove,reject){
var req = XMLHttpRequest();
req.onreadystatechange = function(){
if(req.readystate !== 4) return;
if(req.readystate === 4 && req.status === 200) {
//处理正常情况
resove('req.responseText');
}else{
//处理异常情况
reject('服务器错误');
}
}
req.open('get',url);
req.send();
})
return p;
}
//用实例方法调用封装函数
queryData('http://localhost:3000/data')
.then(function(data){
console.log('data1',data);
//想要使用链式编程需要return返回,并且返回的数据将是下一次then方法参数函数的data参数
return queryData('http://localhost:3000/data1')
})
.then(function(data){
console.log('data2',data);
})
(2)参考1:https://www.cnblogs.com/jwyblogs/p/11962866.html
参考2:https://www.nowcoder.com/questionTerminal/86311e67b9124788b68acdf002da55f8
参考3:https://segmentfault.com/a/1190000015114855utm_source=tag-newest
(二)
、HTML 与 CSS。
-
实现两列布局:左列宽度为 200px,右列占据剩余宽度。要求:
可以不兼容旧浏览器;
不能使绝对定位等特殊定位式;
不能使表格布局;
方法一:float+margin-left
div{
height:200px;
}
.left{
float: left;
width: 200px;
/* background: blue; */
}
.right{
margin-left: 200px;
/* background: red; */
}
方法二:使用 flex
.box{
width:500px;
height:200px;
display: flex;
/* background:black; */
}
.left{
width: 200px;
/* background:pink; */
}
.right{
flex: 1;
/* background:green; */
}
方法三:使用 flex + justify-content:space-between;
.box{
width:500px;
height:200px;
/* background:black; */
display: flex;
justify-content:space-between;
}
.left{
width:200px;
/* background: pink; */
}
.right{
/* background:green; */
}
-
把尺?未知
的元素置于浏览器可区域的正中央,且位置不随滚动变化(须兼容旧浏览器)。 有疑问,尺寸未知是是意思??
写法一:
方法二:
使用position属性的绝对定位
.yuansu{
position: absolute;
margin-left: -200px;
margin-top: -80px;
top: 50%;
left: 50%;
display: block;
z-index: 2000;
}
、JavaScript。
-
使正则表达式检查某段字符串是否为 URL。URL 的认定标准(符合以下条件之):
以「协议://」开头,例如「http://」、「https://」、「ftp://」、「file://」等,但需校验是否为有效协议;
以「//」开头,即跟随协议。
string httpurl = String.Format(@"http://{0}(/{1}(?{2}){
{0,1}}){
{0,1}}
-
假设某个数组的元素全是数字类型,请编写代码找出该数组中的重复元素。要求:
代码中最多只能存在次遍历(数组循环,indexOf、forEach、filter 等法均视为遍历);
不能使 ES>=6 中的新增法和对象。
方法一:
写法一:【正确】
function getRepeat(ele){
var tmp = [];
var d = {};
for(let n of ele){
if(!d[n]){
d[n] = 0;
}
if(n in d && d[n] == 1){
tmp.push(n);
}
d[n] += 1;
}
return tmp;
}
var a = [5, 4, 3, 2, 1, 2, 3, 2, 1];
console.log('取出重复元素',getRepeat(a))
写法二:【正确】
var a = [5, 4, 3, 2, 1, 2, 3, 2, 1];
Array.prototype.duplicate=function() {
var tmp = [];
var d = {};
for(let n of a){
//一个个数测试,d保存的就是同一个数字出现的次数
if(!d[n]){
d[n] = 0;
}
//d[n] == 1的判断是为了确保相同数字只显示一个
if(n in d && d[n] == 1){
tmp.push(n);
}
//判断有没有重复,就算下它出现了几次
d[n] += 1;
}
return tmp;
}
console.log('取出重复元素', a.duplicate())
方法二:(理解)
var a = [5,4,3,2,1,2,3,2,1,];
Array.prototype.duplicate=function() {
var tmp = [];
this.concat().sort().sort(function(a,b){
if(a==b && tmp.indexOf(a) === -1) tmp.push(a);
});
return tmp;
}
console.log('取出重复元素', a.duplicate())
方法三:(理解)不符合本题要求
function remove(arr){
var new_arr = [];
var new_arr2 = [];
arr.forEach((val, index)=> {
//new_arr是对数组去重
if(new_arr.indexOf(val) === -1) {
new_arr.push(val);
} else {
//对重复的元素本身去重
if(new_arr2.indexOf(val) === -1) {
new_arr2.push(val);
}
}
});
return new_arr2;
}
var a = [1,2,4,4,3,3,1,5,3];
var b = remove(a);
console.log('b',b);
-
a、b、c 都是执异步操作的函数,请通过 Promise、async、await 实现:a、b 并执完毕后再执 c。
function a(callback) { setTimeout(callback, 10); }
function b(callback) { setTimeout(callback, 20); }
function c(callback) { setTimeout(callback, 30); }
方法二:
参考:https://blog.csdn.net/qq_39456813/article/details/101191977
(三)
一、单选题
1、下列有关定位说法错误的是? A
A. absolute 可以基于 static 定位
「 生成绝对定位的元素,相对于static 定位以外的第一个父元素
进行定位 」
B. relative 可以基于 absolute 进行定位。
C. fixed 固定悬浮在 视口内
D. 定位 position 不能继承到子元素
2、把鼠标移到按钮并点击时,会产生一串什么样的事件? D
A. active hover focus
B. hover focus active
C. focus hover active
D. hover active focus
3、在 JS 中 ES5 以下哪种情况会产生一个新的作用域? E
A. if 语句
B. 所有选项都会
C. for 语句
D. try……catch块
E. 函数
4、以下代码在控制台打印结果为? D
其中:
// test1.js
console.log('test1')
// test2.js
console.log('test2')
A. test1 inline script test2
B. inline script test1 test2
C. test1 test2 inline script
D. inline script test2 test1
5. 现有如下策略:
Content-Security-Policy: default-src 'self'; script-src *.script1.org; font-src test.org; media-src https:; script-src * .script2.org
在该网页中,以下说法错误的是: C
A. 图片只能从本域获取
B. 多媒体文件必须通过 SSL方式获取
C. 可运行脚本不允许来自于 script2.org或其子域
D. 字体资源允许来自于 test.org 及其子域
解析:
参考:https://www.cnblogs.com/suizhikuo/p/12215229.html
Web 安全之内容安全策略(Content-Security-Policy,CSP)配置
二、多选题:
1、以下哪几个选项的输出结果相同? ABD
A.
for(var i = 0; i < 10; i++){
setTimeout(function(i){
console.log(i)
}, 0, i)
}
B.
for(let i = 0; i < 10; i++){
setTimeout(function(){
console.log(i)
}, 0)
}
C.
for(var i = 0; i < 10; i++){
setTimeout(function(){
console.log(i)
}, 0)
}
D.
for(var i = 0; i < 10; i++){
(function(i){
setTimeout(function(){
console.log(i)
}, 0)
}(i))
}
解析:
ABD 输出 0123456789
C 输出10个10
2、以下哪些说法是正确的?ABCD
A. 所有的引用类型(数组、对象、函数),都具有对象特性,即可自由扩展属性(null 除外)
B. 所有的引用类型(数组、对象、函数),__proto__
属性值指向它的构造函数的 prototype
属性值
C. 所有的引用类型(数组、对象、函数),都有一个 __proto__
属性,属性值是一个普通的对象
D. 所有的函数,都有一个 prototype
属性,属性值也是一个普通的对象
知识点:
原型的5个规则(引用类型指除了null的引用类型 )
(1)所有引用类型(数组,对象,函数),都具有对象特性,即可自由扩展属性
var obj = {}
obj.a=100;
var arr =[]
arr.a =100;
function fn(){}
fn.a=100;
(2)所有的引用类型,都有一个proto属性,属性值是一个普通对象
console.log(obj.__proto__);
console.log(arr.__proto__);
console.log(fn.__proto__);
(3)所有的函数都有一个prototype属性,属性值也是一个普通对象
console.log(fn.prototype)
(4)所有的引用类型(数组,对象函数),proto属性指向他的构造函数的prototype属性
console.log(obj.__proto__===Object.prototype)
(5)当试图得到一个引用类型的某个属性时,如果对象本身没有这个属性,那么会去他的proto(即它的构造函数的prototype)中去寻找
function Foo(name){
this.name= name;
}
Foo.prototype.alertName = function(){
alert(this.name)
}
var f = new Foo('zs');
f.printName=function(){
console.log(this.name)
}
f.printName();
f.alertName();
三、填空题
1、请写出以下表达式的结果: typeof null
类型
结果
Undefined
“undefined”
Null
“object” (见下文)
Boolean
“boolean”
Number
“number”
BigInt(ECMAScript 2020 新增)
“bigint”
String
“string”
Symbol (ECMAScript 2015 新增)
“symbol”
Function 对象 (按照 ECMA-262 规范实现 [[Call]])
“function”
2、请写出以下表达式的结果:
void 0 == null //true
void 0 == undefined //true
void 0 === null //false
void 0 === undefined //true
四、问答题
题目描述
填写一个函数,实现多个异步任务并行时限制最大并行数量的功能,可参考如下函数结构:
async function runParallel(max: number, tasks: (() => Promise)[]) : Promise
使用:
const tasks = [...Array(100).keys()].map(i => () => new Promise(res => setTimeout(res,1000)))
当并行 Promise 的数量达到10个时,就会等待其中某个 resolve 后,才开启队列中下一个 Promise。
五、编程题
1、URL参数提取
时间限制:C/C++ 1秒,其他语言 2 秒
空间限制:C/C++ 32768K,其他语言 65536K
语言限定:JavaScript(V8 6.0.0)
题目描述
实现一个parseURL函数,用于提取URL中的哈希以及查询参数。
输入描述
输一个含有哈希的URL,其中包含若干个查询参数
输出描述
输出有若干行,第一行为URL中的哈希,接下来的每一行为该URL的查询参数名称和值,用空格分隔,它们的顺序按照原本在URL中出现的顺序输出;
输入
http://163.com/role/search/#pagepageSize=20&pageNumber=10
输出
page
pageSize 20
pageNumber 10
方法一:
function parseQueryString(argu){
var str = argu.split('?')[1];
var result = {};
var temp = str.split('&');
for(var i=0; i
方法二:
function getQueryString(name) {
var reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)')
var currentUrl = window.location.search
let r = ''
if (currentUrl != '') {
r = window.location.search.substr(1).match(reg) // 没有#的url
} else {
r = window.location.hash.split('?')[1].match(reg)
}
if (r != null) return unescape(r[2])
return null
}
this.smallId = getQueryString('smallId')
this.supplierId = getQueryString('supplierId')
this.taskDetailId = getQueryString('taskDetailId')
2、实现一个 format 函数,快捷格式化字符串,不用老写 +号连接。
输入描述
输入至少有一行:
第一个行是一个字符串,其中包含一些占位符,占位符格式为:{0}、{1}、{2} 依次类推(花括号内可能含有空格,例如 {1} 是合法的 );
后续行(如果有)为上面的占位符按顺序对应的值。
输出描述
输出只有一行,为格式换后的字符串,将所有占位符替换为对应值;
注:输入输出相关代码已经写好,您只需实现 format 函数即可,该函数返回一个格式化后的字符串。
示例1 输入输出示例仅供调试。
输入
h1 {0},今天是 {1} 年 {2} 月 {3} 号星期 {4}
Tom
2015
6
20
五
输出
hi Tom,今天是2015年6月20号星期五
示例2 输入输出示例仅供调试。
输入
Hello {2}
a
b
示例3 输入输出示例仅供调试。
输入
{0} {2} {2},My {1} is {2}
Hello
name
World
输出
Hello World World,My name is World
JavaScript题:
参考:https://blog.csdn.net/liuyan19891230/article/details/102385743
(四)
一、选择题
1、下面代码的输出是什么? B
const person = {
name:"Lydia",
age:21
};
for(const item in person){
console.log(item);
}
A. {name:“Lydia”},{age:21}
B. "name","age"
C.“Lydia”,21
D. [“name”,“Lydia”],[“age”,21]
解析:
在 for-in 循环中,我们可以通过对象的 key 来进行迭代,也就是这里的 name 和 age。在底层,对象的 key 都是字符串(如果他们不是 Symbol 的话)。在每次循环中,我们将 item 设定为当前遍历到的 key 所以一开始, item 是 name,之后 item 输出的则是 age。
for…in 语句用于遍历数组或者对象的属性,得到数组的下标,对象的属性名。
for…of 遍历(当前对象上的)每一个属性值,与 forEach一样
例如:
var arr=[{name:'张三'},{name:'李四'}];
var obj={name:'王五'};
for (var i in arr){
console.log(i) // 0 1 遍历得到下标
}
for (var i in obj){
console.log(i) // name 得到属性名
}
for (var i of arr){
console.log(i) // {name:'张三'} {name:'李四'} 得到数组的每一项值,与forEach一样
}
2、num 的值是什么? C
const num = parseInt(“7*6”, 10);
A. 42 B. “42” C. 7
D. NaN
解析:
只返回了字符串中第一个字母,设定了 进制 后 (也就是第二个参数,指定需要解析的数字是什么进制: 十进制、十六机制、八进制、二进制等等……), parseInt 检查字符串中的字符是否合法。一旦遇到一个在指定进制中不合法的字符后,立即停止解析并且忽略后面所有的字符。
*
就是不合法的数字字符。所以只解析到 “7”,并将其解析为十进制的 7, num 的值即为 7。
3、下面代码输出的是什么? A
function getInfo(member, year){
member.name = "Lydia";
year = "1998";
}
const person = { name: "Sarah" };
const birthYear = "1997";
getInfo(person, birthYear);
console.log(person, birthYear);
A. {name: "Lydia"}, "1997"
B. {name:“Sarah”}, “1998”
C. {name:“Lydia”}, “1998”
D. {name:“Sarah”}, “1997”
解析:
普通参数都是 值 传递的,而对象则不同,是 引用 传递。所以说, birthYear 是值传递,因为他是个字符串而不是对象。当我们对参数进行值传递时,会创建一份该值的 复制 。(可以参考问题46)
变量 birthYear有一个对 "1997"的引用,而传入的参数也有一个对 “1997” 的引用,但二者的引用并不相同。当我们通过给 year 赋值 “1998” 来更新 year 的值的时候我们只是更新了 year(的引用)。此时 birthYear 仍然是 “1997”。
而 person 是个对象。参数 member 引用与之 相同的 对象。当我们修改 member 所引用对象的属性时, person 的相应属性也被修改了,因为他们引用了相同的对象。person 的 name 属性也变成了 “Lydia”。
4、下面代码的输出是什么? A
const name = "Lydia";
age = 21;
console.log(delete name);
console.log(delete age);
A. false, true
B. “Lydia”, 21
C. true, true
D. undefined, undefined
解析:
delete 操作符返回一个布尔值:true 指删除成功,否则返回 false。 但是通过 var, const 或 let 关键字声明的变量无法用 delete 操作符来删除。
name 变量由 const 关键字声明,所以删除不成功:返回 false。而我们设定 age 等于 21时,我们实际上添加了一个名为 age 的属性给全局对象。对象中的属性是可以删除的,全局对象也是如此,所以 delete age 返回 true。
5、下面代码的输出是什么? D
const numbers = [1,2,3,4,5]
const [y] = numbers;
console.log(y);
A. [[1,2,3,4,5]]
B. [1,2,3,4,5]
C. [1]
D. 1
解析:
通过解构赋值来解析来自对象的数组或属性的值,比如说:[a, b] = [1, 2];
a的值现在是 1, b的值现在是 2。而在题目中: [y] = [1, 2, 3, 4, 5];
也就是说, y等于数组的第一个值就是数字 1,我们输出 y, 返回 1.
6、下面代码输出什么? C
const value = {number: 10};
const multiply = (x = {...value}) => {
console.log(x.number *= 2);
};
multiply();
multiply();
multiply(value);
multiply(value);
A. 20, 40, 80, 160
B. 20, 40, 20, 40
C. 20, 20, 20, 40
D. NaN, NaN, 20, 40
解析:
在ES6中,我们可以使用默认值初始化参数。如果没有给函数传参,或者传的参值为 “undefined”,那么参数的值将是默认值。上述例子中,我们将 value 对象进行了解构并传到一个新对象中,因此 x 的默认值为 {number:10} 。
默认参数在调用时才会进行计算,每次调用函数时,都会创建一个新的对象。我们前两次调用 multiply 函数且不传递值,那么每一次 x 的默认值都为 {number:10} ,因此打印出该数字的乘积值为 20。
第三次调用 multiply 时,我们传递了一个参数,即对象 value。*=
运算符实际上是 x.number=x.number*2
的简写,我们修改了 x.number
的值,并打印出值 20。
第四次,我们再次传递 value对象。x.number
之前被修改为 20
,所以 x.number*=2
打印为 40
。
7、下面代码输出什么? D
[1,2,3,4].reduce((x,y)=> console.log(x,y));
A. 12 and 33 and 64
B. 12 and 23 and 34
C. 1undefined and 2undefined and 3undefined and 4undefined
D. 1 2
and undefined 3
and undefined 4
解析:
语法:
array.reduce(function(total, currentValue, currentIndex, arr), initialValue)
reduce 函数接收4个参数:
total:必需。初始值, 或者计算结束后的返回值。
currentValue:必需。当前元素。
currentIndex:可选。当前元素的索引。
arr:可选。当前元素所属的数组对象。
reduce 函数的返回值将会分配给累计器,该返回值在数组的每个迭代中被记住,并最后成为最终的单个结果值。
reduce 函数还有一个可选参数 initialValue, 该参数将作为第一次调用回调函数时的第一个参数的值。如果没有提供 initialValue,则将使用数组中的第一个元素。
在上述例子, reduce方法接收的第一个参数(Accumulator)是 x
, 第二个参数(Current Value)是 y
。
在第一次调用时,累加器 x
为 1
,当前值 “y”
为 2
,打印出累加器和当前值:1
和 2
。
例子中我们的回调函数没有返回任何值,只是打印累加器的值和当前值。如果函数没有返回值,则默认返回 undefined。在下一次调用时,累加器为 undefined
,当前值为“3”
, 因此 undefined
和 3
被打印出。
在第四次调用时,回调函数依然没有返回值。累加器再次为 undefined
,当前值为“4”
。undefined
和 4
被打印出。
8、使用哪个构造函数可以成功继承 Dog 类? B
class Dog{
constructor(name){
this.name = name;
}
}
class Labrador extends Dog{
// 1
constructor(name, size){
this.size = size;
}
// 2
constructor(name,size){
super(name);
this.size = size;
}
// 3
constructor(size){
super(name);
this.size = size;
}
// 4
constructor(name,size){
this.name = name;
this.size = size;
}
}
A. 1 B. 2
C. 3 D. 4
解析:
在子类中,在调用 super 之前不能访问到 this
关键字。如果这样做,它将抛出一个 ReferenceError
:1和4将引发一个引用错误。
使用 super
关键字,需要用给定的参数来调用父类的构造函数。父类的构造函数接收 name
参数,因此我们需要将 name
传递给 super
。
Labrador
类接收两个参数, name
参数是由于它继承了 Dog
, size
作为 Labrador
类的额外属性,它们都需要传递给 Labrador
的构造函数,因此使用构造函数2
正确完成。
9、如何能打印出 console.log 语句后注释掉的值? C
function* startGame(){
const answer = yield "Do you love JavaScript?";
if(answer !== "Yes"){
return "Oh wow... Guess we're gone here";
}
return "JavaScript loves you back ??";
}
const game = startGame();
console.log(/* 1 */); // Do you love JavaScript?
console.log(/* 1 */) // JavaScript loves you back ??
A. game.next("Yes").value
and game.next().value
B. game.next.value("Yes")
and game.next.value()
C. game.next().value
and game.next("Yes").value
D. game.next.value()
and game.next.value("Yes")
解析:
generator 函数在遇到 yield
关键字时会“暂停”其执行。首先,我们需要让函数产生字符串 Doyou loveJavaScript,这可以通过调用 game.next().value
来完成。上述函数的第一行就有一个 yield
关键字,那么运行立即停止了, yield
表达式本身没有返回值,或者说总是返回 undefined
, 这意味着此时变量 answer
为 undefined
next
方法可以带一个参数,该参数会被当作上一个 yield
表达式的返回值。当我们调用 game.next("Yes").value
时,先前的 yield
的返回值将被替换为传递给 next()
函数的参数 "Yes"
。此时变量 answer
被赋值为 "Yes"
, if语句返回 false
,所以 JavaScriptloves you back??
被打印。
10、下面代码输出什么? A
async function getData(){
return await Promise.resolve("I made it!");
}
const data = getData();
console.log(data);
A. “I made it!”
B. Promise(< pending>)
C. Promise(< resolved>:“I made it!”)
D. undefined
解析:
异步函数始终返回一个promise
。 await
仍然需要等待promise
的解决:当我们调用 getData()
并将其赋值给 data
,此时 data
为 getData
方法返回的一个挂起的 promise
,该 promise
并没有解决。
如果我们想要访问已解决的值 "I made it!"
,可以在 data
上使用 .then()
方法:data.then(res=>console.log(res))
这样将打印 "I made it!"
二、问答题
1、var、let const的区别?
var 定义的变量,没有块的概念,可以跨块访问, 不能跨函数访问。 let
定义的变量,只能在块作用域里访问,不能跨块访问,也不能跨函数访问。 const
用来定义常量,使用时必须初始化(即必须赋值),只能在块作用域里访问,而且不能修改。
// 块作用域
{
var a = 1;
let b = 2;
const c = 3;
// c = 4; // 报错
var aa;
let bb;
// const cc; // 报错
console.log(a); // 1
console.log(b); // 2
console.log(c); // 3
console.log(aa); // undefined
console.log(bb); // undefined
}
console.log(a); // 1
// console.log(b); // 报错
// console.log(c); // 报错
// 函数作用域
(function A() {
var d = 5;
let e = 6;
const f = 7;
console.log(d); // 5
console.log(e); // 6
console.log(f); // 7
})();
// console.log(d); // 报错
// console.log(e); // 报错
// console.log(f); // 报错
延伸:const定义的对象属性是否可以改变:可以改变
参考:https://blog.csdn.net/unionz/article/details/80032048
const person = {
name : 'jiuke',
sex : '男'
}
person.name = 'test'
console.log(person.name) //test
person对象的name属性确实被修改了
因为对象是引用类型的,person中保存的仅是对象的指针,这就意味着,const仅保证指针不发生改变,修改对象的属性不会改变对象的指针,所以是被允许的。也就是说const定义的引用类型只要指针不发生改变,其他的不论如何改变都是允许的。
2、vue中key值的作用是什么?
渲染列表时,key值需要一个唯一确定的id来赋值。
- key是为每个vnode指定唯一的id,在同级vnode的Diff过程中,可以根据key快速的进行对比,来判断是否为相同节点。
- 利用 key 的唯一性生成 map 对象来获取对应节点,比遍历方式更快,指定key后,可以保证渲染的准确性(尽可能的复用 DOM 元素。)
参考资料:https://www.jianshu.com/p/a634eb3c19c2
3、简单介绍 Promise
参考:https://www.jianshu.com/p/c8ebbc7d4c01
Promise 是一个对象,保存着未来将要结束的事件,她有两个特征:
- 对象的状态不受外部影响,Promise对象代表一个异步操作,有三种状态,pending进行中,fulfilled已成功,rejected已失败,只有异步操作的结果,才可以决定当前是哪一种状态,任何其他操作都无法改变这个状态,这也就是promise名字的由来
- 一旦状态改变,就不会再变,promise对象状态改变只有两种可能,从pending改到fulfilled或者从pending改到rejected,只要这两种情况发生,状态就凝固了,不会再改变,这个时候就称为定型resolved,
Promise的基本用法:
let promise1 = new Promise(function(resolve,reject){
setTimeout(function(){
resolve('ok')
},1000)
})
promise1.then(function success(val){
console.log(val)
})
最简单代码实现promise
class PromiseM {
constructor (process) {
this.status = 'pending'
this.msg = ''
process(this.resolve.bind(this), this.reject.bind(this))
return this
}
resolve (val) {
this.status = 'fulfilled'
this.msg = val
}
reject (err) {
this.status = 'rejected'
this.msg = err
}
then (fufilled, reject) {
if(this.status === 'fulfilled') {
fufilled(this.msg)
}
if(this.status === 'rejected') {
reject(this.msg)
}
}
}
测试代码:
var mm=new PromiseM(function(resolve,reject){
resolve('123');
});
mm.then(function(success){
console.log(success);
},function(){
console.log('fail!');
});
4、computed 和 watched 的区别是什么?
共同点:
- 都是监听数据变化的方法,用来观察和响应Vue实例上的数据变动。
- 可以将复杂的逻辑放入到计算属性和watch 中,从而使得代码更加整齐,分工明确。
不同点:
- computed
- 类型:{ [key: string]: Function | { get: Function, set: Function } }
- 计算属性将被混入到 Vue 事例中。所有 getter 和 setter 的 this 上下文自动地绑定为 Vue 实例。但是如果使用了箭头函数,那么此时使用 this 就不会指向组件实例了。而且计算的结果是会被缓存的,除非依赖的响应式属性变化才会重新计算,这无疑在性能上更加友好。
- 计算属性将变量的 get 属性重写成了定义的函数方法,实现了数据劫持。
- 计算属性可以监听 data 和 props 里的值,只有当被监听的数据发生改变时,才会重新计算,从而得到一个新值。
- 场景:一个数据受到多个数据的影响
- watch
- 类型:{ [key: string]: string | Function | Object | Array }
- 那这里 key 是咱们要监听的数据,值可以是回调函数,方法名或者对象。同样的如果在watch 中使用箭头函数,则不要使用 this ,因为此时它不会指向 Vue 实例。
- 会产生一个 watcher 对象,在监视的属性每次变动时都会触发回调
- 监听 data 里的已知变量的变化
- 场景:一个数据影响多个数据
5、请详细说下你对 Vue 生命周期的理解?
vue生命周期总共分为8个阶段创建前/后,载入前/后,更新前/后,销毁前/后。
-
创建前/后:在beforeCreated阶段,vue实例的挂载完el还没有。
-
载入前/后:在beforeMount阶段,vue实例的$el和data都初始化了,但还是挂载之前为虚拟的dom节点,data.message还未替换。在mounted阶段,vue实例挂载完成,data.message成功渲染。
-
更新前/后:当data变化时,会触发beforeUpdate和updated方法。
-
销毁前/后:在执行destroy方法后,对data的改变不会再触发周期函数,说明此时vue实例已经解除了事件监听以及和dom的绑定,但是dom结构依然存在。
export default {
data(){
return {
ptext:“测试文本”
}
},
beforeCreate(){
console.log(‘enter beforeCreate’)
},
created(){
console.log(‘enter created’)
},
beforeMount(){
console.log(‘enter beforeMount’)
},
mounted(){
console.log(‘enter mounted’)
},
beforeDestroy(){
console.log(‘enter beforeDestroy’)
},
destroyed(){
console.log(‘enter destroyed’)
}
}
补充:谈谈你对vue的双向数据绑定原理的理解
vue.js 是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。
具体步骤:
- 第一步:需要observe的数据对象进行递归遍历,包括子属性对象的属性,都加上 setter和getter。这样的话,给这个对象的某个值赋值,就会触发setter,那么就能监听到了数据变化
- 第二步:compile解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图
- 第三步:Watcher订阅者是Observer和Compile之间通信的桥梁,主要做的事情是:
- 在自身实例化时往属性订阅器(dep)里面添加自己
- 自身必须有一个update()方法
- 待属性变动dep.notice()通知时,能调用自身的update()方法,并触发Compile中绑定的回调,则功成身退。
- 第四步:MVVM作为数据绑定的入口,整合Observer、Compile和Watcher三者,通过Observer来监听自己的model数据变化,通过Compile来解析编译模板指令,最终利用Watcher搭起Observer和Compile之间的通信桥梁,达到数据变化 -> 视图更新;视图交互变化(input) -> 数据model变更的双向绑定效果。
简而言之,就是先转化成AST树,再得到的render函数返回VNode(Vue的虚拟DOM节点)
6、介绍一下你对浏览器内核的理解?
主要分成两个部分:渲染引擎(Render Engine)和JS引擎。
- 渲染引擎:负责取得网页的内容(html,xml和图像等),整理讯息(例如假如css),以及计算网页的显示方式,然后输出到显示器或打印机。浏览器的内核的不同对于网页的语法解释会有不同,所以渲染的效果也不同。所有网页浏览器、电子邮件客户端以及它需要编辑、显示网络内容的应用程序都需要内核。
- JS引擎:解析和执行JavaScript来实现网页的动态效果。<
相关文章
动力学技术KTU1121 USB Type-C 端口保护器的介绍、特性、及应用
Sensata PTE7300密封数字压力传感器的介绍、特性、及应用
PANJIT PBHV8110DA/PBHV9110DA低Vce(sat)晶体管的介绍、特性、及应用
ams OSRAM OSLON 黑色平板X LED器件的介绍、特性、及应用
Cree LED CLQ6A三合一贴片LED的介绍、特性、及应用
Cree LED CLQ6B 4-in-1 RGBW贴片LED的介绍、特性、及应用
NDK NX1210AB表面贴装晶体的介绍、特性、及应用
伊顿ACE2V3225共模芯片电感器的介绍、特性、及应用
意法半导体X040灵敏型栅可控硅和Z040可控硅的介绍、特性、及应用
ABLIC S-82Y1B电池保护芯片的介绍、特性、及应用