8.11 & 8.12筆試
順豐科技二面 8.11(視訊)
- 自我介紹 + 實習經歷介紹
- 未來職業規劃
- 為什麼想來順豐
- 瞭解過順豐科技的業務嗎?為什麼沒關注?
- 學習工作中遇到過什麼比較困難的事情?怎麼解決?
- 期望薪資?能降嗎?
- 提問
猿輔導現場二面 8.10
- 自我介紹 + 實習經歷
- 演算法題1:使用setTimeout實現setInterval功能
function mySetInterval(fn, ms) {
function interval(){
setTimeout(interval, ms);
fn();
}
setTimeout(interval, ms);
}
// 更好的實現
function mySetInterval(fn, ms, count) {
function interval() {
if(typeof count === 'undefined' || count-- > 0) {
setTimeout(interval, ms);
try {
fn();
} catch(e) {
t = 0;
throw e.toString();
}
}
}
setTimeout(interval, ms);
}
複製程式碼
- 演算法題2:使用遞迴和非遞迴兩種方法統計一棵二叉樹的深度
- 演算法題3:在一個無序陣列中,尋找連續兩個及兩個以上的相同元素的個數
例如:有陣列[10, 22, 32, 4, 4, 5, 6, 9, 8, 8, 2],返回結果為2
4, 4
8, 8
複製程式碼
- 演算法題3延伸:如果給定的是一個有序陣列,如何優化?
猿輔導現場一面 8.10
- 自我介紹 + 實習經歷
- 為什麼會選前端
- React:生命週期 + 元件間資料傳遞 + ref
- 原生JS:作用域鏈 + 原型鏈 + 閉包
- 快取機制(強快取 + 協商快取) + 相關HTTP頭部資訊
- URL從輸入網址到最終呈現的流程 + 哪些步驟可以優化
- Canvas API
- 演算法題:判斷一棵二叉樹是對稱的
CVTE終面(視訊) 8.10
太多了,跟面試前補充的簡歷幾乎一樣
順豐科技一面 8.4 (視訊)
- 自我介紹 + 專案 + 實習
- 研究生之後記憶深刻的事
- 原型的理解
- 閉包的理解
- ES5/ES6建立物件的方法
- ES5/ES6繼承的方法
- 前端語言體系三要素
- HTML語義化
根據內容的結構化(內容語義化),選擇合適的標籤(程式碼語義化),
便於開發者閱讀和寫出更優雅的程式碼的同時讓瀏覽器的爬蟲和機器很好地解析
複製程式碼
- React的Virtual Dom/Diff演算法
- Set和Map區別
- HTTP與TCP/IP或Socket的區別
TCP連線 需要經過“三次握手”
HTTP/1.0 客戶端傳送的每次請求都需要伺服器回送響應,在請求結束後,會主動釋放連線
HTTP/1.1 一次連線中處理多個請求,並且多個請求可以重疊進行,不需要等待一個請求結束後再傳送下一個請求
複製程式碼
- 為什麼投順豐科技
- 提問
補充:
- 前端相容性問題
- Webpack更新原理
- CSS Hack
- 箭頭函式為什麼不能作為建構函式
- npm釋出版本號
- package.json和package-lock.json區別
- CSS菊花圖(animation-delay實現)
- Object.create實現
大疆二面 8.2(視訊)
- 自我介紹
- 工作過程中遇到什麼困難?
- 怎麼跟產品經理溝通的?出現問題的原因在哪裡?如何解決?最終達成一致的立足點是什麼?
- 如何學習的?寫過元件庫嗎?看過或自己有開源專案嗎?
- 瞭解React Native嗎
- 瞭解C/C++嗎?(懂Java)那說說Java記憶體機制?
- 資料結構瞭解?哪些排序演算法?實際中有用嗎?
- 你希望的工作環境是怎樣的?
- 你對996制度怎麼看?
- 你有什麼問題?
CVTE二面 8.1(視訊)
- 自我介紹
- 前端的學習曲線
- 對自己未來的規劃
- React的diff演算法
- React生命週期,setState能用在componentWillUpdate裡嗎?
- 演算法題:字串最長不重複子串的長度
- 最近看什麼新知識
CVTE一面 7.29
- 最近做什麼專案?
- 跨域瞭解過哪些?跨域傳cookie可以使用哪種方法?
第一步:
設定響應訊息頭Access-Control-Allow-Credentials值為“true”。
同時,還需要設定響應訊息頭Access-Control-Allow-Origin值為指定單一域名
第二步:
客戶端需要設定Ajax請求屬性withCredentials=true,讓Ajax請求都帶上Cookie
複製程式碼
- 怎麼處理cookie?刪除cookie?
- insertBefore方法
- JS非同步解決方案
- 瀏覽器端的EventLoop和Node的EventLoop
- setTimeout和Ajax的優先順序
- JS實現事件繫結的方法
1. elementObject.addEventListener(eventName,handle,useCapture)
2. elementObject.attachEvent(eventName,handle);(僅支援IE8及以下)
3. document.getElementById("demo").onclick = function(){};
複製程式碼
- CSS如何適配瀏覽器大小?
1. <meta name="viewport"
content="width=device-width,
height=device-height,
inital-scale=1.0,
maximum-scale=1.0,
user-scalable=no;"
/>
2. 網頁內部的元素寬度要使用百分比,在不同的螢幕大小下需使用媒體查詢定義不同的css程式碼
程式碼解析:
width:控制 viewport 的大小,可以是指定的一個值,比如 1920,或者是特殊的值,如 device-width 為裝置的寬度,單位為縮放為 100% 時的 CSS 的畫素。
height:和 width 相對應,指定高度,可以是指定的一個值,比如 1080,或者是特殊的值,如 device-height 為裝置的高度。
initial-scale:初始縮放比例,即當頁面第一次載入是時縮放比例。
maximum-scale:允許使用者縮放到的最大比例。
minimum-scale:允許使用者縮放到的最小比例。
user-scalable:使用者是否可以手動縮放。
複製程式碼
- 瀏覽器的核心有哪些?哪些CSS屬性需要設定?
1. IE: Trident核心
2. Firefox:Gecko核心
3. Google:Blink核心
4. Safari:Webkit核心
-webkit- ,針對safari,chrome瀏覽器的核心CSS寫法
-moz-,針對firefox瀏覽器的核心CSS寫法
-ms-,針對ie核心的CSS寫法
-o-,針對Opera核心的CSS寫法
複製程式碼
- 三列布局的實現
- 三角形的實現
- HTTP/1.1和HTTP/1.0的區別
- HTTP的狀態碼
- 瀏覽器快取的分類
- cache-control的值有哪些?
- 對JS原型鏈的理解
- 實現方法?弊端?
- 平常怎麼學前端?
- 為什麼選擇前端?
- 有寫部落格嗎?有給開源專案貢獻程式碼嗎?
阿里飛豬二面 7.25
- 自我介紹 + 實習情況
- 如何判斷Array
- React-router原理?用到哪些API?
- React實現幻燈片元件對外暴露的介面有哪些?
- CSS第三方庫的原理
- CSS響應式設計的方式
CSS響應式設計適配多種裝置:
1. <meta name="viewport" content="width=device-width, initial-scale=1" />
2. 不要使用絕對寬度
3. 字型大小和長寬邊距屬性不要用”px”,應該用相對大小的“rem”
4. 使用流動佈局
- 如果寬度太小,放不下兩個元素,後面的元素會自動滾動到前面元素的下方,不會在水平方向overflow(溢位),避免了水平滾動條的出現
5. link標籤的media屬性
- <link rel="stylesheet" type="text/css" media="screen and (min-width: 600px) and (max-device-width: 980px)"
href="css600-980.css" />
6. Media Query
7. 圖片的自適應(自動縮放)
- img{max-width: 100%;}
- 最好還是根據不同大小的螢幕,載入不同解析度的圖片
複製程式碼
- XSS和CORS原理及解決方案
- HTTP和HTTPS的區別
- 前端優化的方法
- utf-8和unicode區別
1. Unicode是'字符集',utf-8是'編碼規則'
(以8位為一個編碼單位的可變長編碼,會將一個碼位編碼為1到4個位元組)
2. 字符集:為每一個字元分配一個唯一的ID
編碼規則:將碼位轉為位元組序序列的規則
例如:'知'的碼位是30693,記作U+7735(16進製為0x77E5)
'Unicode與utf-8關係':
U+0000 - U+007F:0XXXXXXX
U+0080 - U+07FF:110XXXXX 10XXXXXX
U+0800 - U+FFFF:1110XXXX 10XXXXXX 10XXXXXX
U+10000 - U+1FFFF:11110XXX 10XXXXXX 10XXXXXX 10XXXXXX
3. utf-16使用2或4位元組來儲存,utf-32使用4位元組儲存
'GBK與utf-8':
- UTF-8包含全世界所有國家需要用到的字元,是國際編碼,通用性強。
- GBK是國家標準GB2312基礎上擴容後相容GB2312的標準。
- GBK的文字編碼是用'雙位元組'來表示的,為了區分中文,將其最高位都設定成1,通用性比UTF8差。
複製程式碼
大疆一面 7.24
- 自我介紹
- React-router原理 + SPA
- React-router的Link和a標籤的跳轉有什麼區別?
- Link按需更新,a標籤會重複渲染、造成白屏
補充: React-router中的Link和Route的區別
- route是配置,link是使用
- 靜態跳轉:通過Link元件實現靜態跳轉
- 必須以寫入元件的方式實現跳轉
- 無法跳到歷史記錄中的前一個介面,後一個介面,前N個介面,後N個介面
- 動態跳轉:通過Route注入component中的route屬性實現動態跳轉
- 從Route元件中傳入component中的router屬性物件解決了這個問題
- NavLink是Link的一個特定版本,會在匹配上當前的url的時候給已經渲染的元素新增引數
- state和props的區別
- virtual DOM和diff演算法
- Ajax請求應該在哪個生命週期函式中?為什麼不在componentWillMount裡?
- 在componentDidMount中
- ajax請求在返回資料後需呼叫setState方法,進而更新state值,但componentWillMount並不允許(該函式效果與constructor相同,只能設定state初始值),無法觸發重渲染
- key有什麼作用?為什麼不應該用陣列的index值當key?(不穩定)
- 演算法題:在區域性遞增陣列(如:[3, 4, 5, 1, 2])中查詢指定數字是否存在
- React學了多久?
- 提問
拼多多筆試 7.22
- 下面的輸出是什麼?
const promise = new Promise((resolve, reject) => {
console.log('a');
resolve();
console.log('b');
});
promise.then(() => {
console.log('c');
});
console.log('d');
// a b d c,Promise的非同步執行
複製程式碼
- 下面的輸出是什麼?
(function(x) {
return (function(y) {
console.log(x);
})(2);
})(1);
// 1,閉包問題
複製程式碼
- 下面的輸出是什麼?
pi = 0;
radius = 1;
function circum(radius) {
radius = 3;
pi = 3.14;
console.log(2 * pi * radius); // 18.14
console.log(arguments[0]); // 3
}
circum(2);
console.log(pi); // 3.14
console.log(radius); // 1
函式內修改了radius 修改的是"形式引數",修改的pi是"全域性的"pi
複製程式碼
與下述情況相同
var pi = 0;
var radius = 1;
function circum(radius) {
radius = 3;
pi = 3.14;
console.log(2 * pi * radius); // 18.84
console.log(arguments[0]); // 3
}
circum(radius);
console.log(pi); // 3.14
console.log(radius); // 1
複製程式碼
補充
function foo(a, b){
arguments[0] = 9;
arguments[1] = 99;
console.log(a, b); //9, 99
}
foo(1, 10);
function foo(a, b){
a = 8;
b = 88;
console.log(arguments[0], arguments[1]); //8, 88
}
foo(1, 10);
// ES6的預設函式不會改變arguments類陣列物件值
function foo(a=1, b=10){
arguments[0] = 9;
arguments[1] = 99;
console.log(a, b); //1, 10
}
foo();
// 例項
function f2(a) {
console.log(a);
var a;
console.log(a);
console.log(arguments[0])
}
f2(10)
經過變數提升後:
function f2(a) {
var a;
console.log(a);
console.log(a);
console.log(arguments[0])
}
f2(10);
var a會被歸納,由於a已經有值,故不會變為undefined
複製程式碼
- 哪些是穩定排序?哪些是不穩定排序?
定義:
假定在待排序的記錄序列中,存在多個具有相同的關鍵字的記錄,若經過排序,這些記錄的相對次序保持不變,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序後的序列中,r[i]仍在r[j]之前,則稱這種排序演算法是穩定的;否則稱為不穩定的。
穩定排序:
插入排序 [1] ,氣泡排序 [2] ,歸併排序 [3] ,基數排序 [4] ,計數排序 [5]
不穩定排序:
快速排序 [1],簡單選擇排序 [2],希爾排序 [3],堆排序 [4]
複製程式碼
- typeof obj === 'object'判斷obj是物件的弊端?如何改進?
var obj = {};
var arr = [];
var funcInstance = new (function (){});
var isNull = null;
console.log(typeof obj === 'object'); //true
console.log(typeof arr === 'object'); //true
console.log(typeof funcInstance == 'object'); //true
console.log(typeof isNull === 'object'); // true
// constructor
({}).constructor === Object; //true
([]).constructor === Array; //true
// instanceof
({}) instanceof Object; //true
([]) instanceof Array; //true
// toString: 將當前物件以字串的形式返回
console.log(Object.prototype.toString.call(obj)); //[object Object]
console.log(Object.prototype.toString.call(arr)); //[object Array]
console.log(Object.prototype.toString.call(null)); //[object Null]
複製程式碼
- 補充題:下面的輸出是什麼?
var a = {};
var b = {name:"ZS"};
var c = {};
c[a] = "demo1";
c[b] = "demo2";
console.log(c[a]); // demo2
console.log(c); // Object {[object Object]: "demo2"}
c[a]、c[b]隱式的將物件a,b使用了toString()方法進行了轉換,然後再對屬性賦值。
即:Object.prototype.toString.call(a) ==> [object Object]
因此,c = { [object Object]: 'demo1'} ==> c = {[object Object]: 'demo2' }
複製程式碼
- 程式設計:實現log函式
function log() {
// var arr = [].slice.call(arguments);
var arr = Array.from(arguments);
var res = '';
arr.forEach(elem => {
res += elem + ' ';
});
console.log(`(app)${res}`);
}
// 測試
log('hello', 'world');
log('hello world');
複製程式碼
- 將具有父子關係的原始資料格式化成樹形結構資料
第一種思路:
function toTreeData(data){
var pos={};
var tree=[];
var i=0;
while(data.length!=0){
if(data[i].pid==0){
tree.push({
id:data[i].id,
text:data[i].text,
children:[]
});
pos[data[i].id]=[tree.length-1];
data.splice(i,1);
i--;
}else{
var posArr=pos[data[i].pid];
if(posArr!=undefined){
var obj=tree[posArr[0]];
for(var j=1;j<posArr.length;j++){
obj=obj.children[posArr[j]];
}
obj.children.push({
id:data[i].id,
text:data[i].text,
children:[]
});
pos[data[i].id]=posArr.concat([obj.children.length-1]);
data.splice(i,1);
i--;
}
}
i++;
if(i>data.length-1){
i=0;
}
}
return tree;
}
var data=[
{id:1,pid:0,text:'A'},
{id:2,pid:4,text:"E[父C]"},
{id:3,pid:7,text:"G[父F]"},
{id:4,pid:1,text:"C[父A]"},
{id:5,pid:6,text:"D[父B]"},
{id:6,pid:0,text:'B'},
{id:7,pid:4,text:"F[父C]"}
];
var result = toTreeData(data);
console.log(result);
複製程式碼
第二種思路:
function treeObj(originObj) {
// 深拷貝
let obj = {};
for(key in originObj) {
var val = originObj[key];
// arguments的callee屬性指向擁有這個 arguments 物件的函式
obj[key] = typeof val === 'object'? arguments.callee(val):val;
}
obj['children'] = [];
return obj;
}
function toTreeData(data, attributes) {
let resData = data;
let tree = [];
// 找根節點
for(let i = 0; i < resData.length; i++) {
if(resData[i][attributes.parentId] === ''
||
resData[i][attributes.parentId] === null) {
tree.push(treeObj(resData[i]));
// 刪除掉已經放入tree中的根節點
resData.splice(i, 1);
i--;
}
}
// 找尋子樹
return run(tree);
function run(childArr) {
if(resData.length !== 0) {
for(let i = 0; i < childArr.length; i++) {
for(let j = 0; j < resData.length; j++) {
if(childArr[i][attributes.id] === resData[j][attributes.parentId]) {
let obj = treeObj(resData[j]);
childArr[i].children.push(obj);
// 刪除加入樹中的節點
resData.splice(j, 1);
j--;
}
}
run(childArr[i].children);
}
}
return tree;
}
}
let allRes = [
{
id: 4,
resName: "刪除角色",
parentId: 2
},
{
id: 3,
resName: "編輯角色",
parentId: ''
},
{
id: 2,
resName: "設定許可權",
parentId: ''
},
{
id: 5,
resName: "新增使用者",
parentId: 4
},
{
id: 6,
resName: "更新使用者",
parentId: 4
},
{
id: 7,
resName: "刪除使用者",
parentId: 6
},
{
id: 8,
resName: "重置密碼",
parentId: 3
},
{
id: 9,
resName: "新增地區",
parentId: 5
},
{
id: 10,
resName: "編輯地區",
parentId: 6
}
];
let data = allRes;
// 屬性配置資訊
let attributes = {
id: 'id',
parentId: 'parentId',
};
let treeData = toTreeData(data, attributes);
console.log(treeData);
複製程式碼
- 深拷貝與淺拷貝的區別?如何實現?
- 淺拷貝(shallow copy):只複製指向某個物件的指標,而不復制物件本身,新舊物件共享一塊記憶體
- 深拷貝(deep copy):複製並建立一個一摸一樣的物件,不共享記憶體,修改新物件,舊物件保持不變
// 1. Object.assign
let foo = {
a: 1,
b: 2,
c: {
d: 1,
}
}
let bar = {};
Object.assign(bar, foo);
foo.a++;
foo.a === 2 //true
bar.a === 1 //true
foo.c.d++;
foo.c.d === 2 //true
bar.c.d === 1 //false
bar.c.d === 2 //true
Object.assign()是一種可以對非巢狀物件進行深拷貝的方法;
如果物件中出現巢狀情況,那麼其對被巢狀物件的行為就成了普通的淺拷貝。
// 2. JSON.parse和JSON.stringify
var obj1 = { body: { a: 10 } };
var obj2 = JSON.parse(JSON.stringify(obj1));
obj2.body.a = 20;
console.log(obj1); // { body: { a: 10 } }
console.log(obj2); // { body: { a: 20 } }
console.log(obj1 === obj2); // false
console.log(obj1.body === obj2.body); // false
用JSON.stringify把物件轉成字串,再用JSON.parse把字串轉成新的物件。
但是,會破壞原型鏈,並且無法拷貝屬性值為function的屬性
// 3. 遞迴
var json1={
"name":"shauna",
"age":18,
"arr1":[1,2,3,4,5],
"string":'got7',
"arr2":[1,2,3,4,5],
"arr3":[{"name1":"shauna"},{"job":"web"}]
};
var json2={};
function copy(obj1,obj2){
var obj2 = obj2 || {};
for(var name in obj1){
if(typeof obj1[name] === "object"){
obj2[name]= (obj1[name].constructor===Array)?[]:{};
copy(obj1[name],obj2[name]);
}else{
obj2[name]=obj1[name];
}
}
return obj2;
}
json2=copy(json1,json2)
json1.arr1.push(6);
alert(json1.arr1); //123456
alert(json2.arr1); //12345
複製程式碼
- 補充題: 陣列扁平化
// 遞迴
function flatten(arr){
var res = [];
for(var i=0;i<arr.length;i++){
if(Array.isArray(arr[i])){
res = res.concat(flatten(arr[i]));
}else{
res.push(arr[i]);
}
}
return res;
}
複製程式碼
CVTE筆試 7.20
-
回溯演算法
-
下面的輸出是什麼?
var array1 = Array(3);
array1[0] = 2;
var result = array1.map(elem => '1');
// ['1', empty * 2]
複製程式碼
- 下面的輸出是什麼?
var setPerson = function(person) {
person.name = 'kevin';
person = {name: 'Nick'};
console.log(person.name); // Nick
person.name = 'Jay';
console.log(person.name); // Jay
}
var person = {name: 'Alan'};
setPerson(person);
console.log(person.name); // Kevin
複製程式碼
- 下面的輸出是什麼?
var execFunc = () => console.log('a');
setTimeout(execFunc, 0);
console.log('000');
execFunc = () => console.log('b');
// '000', 'a'
複製程式碼
補充:setTimeout無法使用含參函式引數
window.setTimeout(hello(userName),3000);
這將使hello函式立即執行,並將'返回值'作為呼叫控制程式碼傳遞給setTimeout函式
// 方法1:
使用'字串形式'可以達到想要的結果:
window.setTimeout("hello(userName)",3000);
但是,此處的username變數必須處於全域性環境下
// 方法2:
function hello(_name){
alert("hello,"+_name);
}
// 建立一個函式,用於返回一個無引數函式
function _hello(_name){
return function(){
hello(_name);
}
}
window.setTimeout(_hello(userName),3000);
使用_hello(userName)來返回一個不帶引數的函式控制程式碼,從而實現了引數傳遞的功能
複製程式碼
- 下面的輸出是什麼?
for(var i = {j: 0}; i.j < 5; i.j++) {
(function(i){
setTimeout(function() {
console.log(i.j);
}, 0);
})(JSON.parse(JSON.stringify(i)));
}
// 0, 1, 2, 3, 4
for(var i = {j: 0}; i.j < 5; i.j++) {
(function(i){
setTimeout(function() {
console.log(i.j);
}, 0);
})(i);
}
// 5, 5, 5, 5, 5
複製程式碼
- Generator
- line-height
- FileReader
- 如何判斷x為陣列
- npm依賴包的版本號
- attachEvent, detachEvent, dispathEvent
阿里飛豬1面 7.20
- 自我介紹 + 專案介紹(難點)
- React相關知識
- 生命週期
- diff演算法及時間複雜度
- refs
- React-router原理
- Redux和Relay的瞭解
- Node的瞭解
- Webpack的瞭解(如何使用)
- typeof操作符的返回值(undefined, number, string, boolean, object, function, symbol)
- ES6的瞭解程度
- var,let,const區別
- const a = []; a.push(1); 允許嗎?
- Symbol的用處(避免屬性名衝突)
- 除get和post之外,HTTP的請求方法還有哪些
- HTTP的狀態碼有哪些
- 從輸入網址到頁面呈現的過程
- DOMContentLoad和Load的區別
- Flex佈局
- 客戶端的儲存方法?localStroage和sessionStorage的區別
- HTML5點選延遲事件
- 移動端前端開發的瞭解
- 前端效能優化的方法
- 前端效能工具
- 平常怎麼學習前端?
- 提問
作業幫7.9
- Webpack的瞭解
- React的diff演算法
- 單向資料流
- MVC和MVVM的區別
- ES6瞭解程度(let,const,Generator,Promise)
- JS非同步方法及回撥地獄
- Node、MongoDB瞭解多少
- 關係型和菲關係型資料庫的區別
- CSS的position屬性有哪些
- 左右定寬,中間自適應的多種CSS佈局方法
- 前端的效能優化
- HTTP狀態碼
作業幫 2018
1.如何進行輸入去重?
// 方法1:
function nonDup(arr){
var res = [];
for(var i = 0; i < arr.length; i++) {
if(res.indexOf(arr[i]) === -1) {
res.push(arr[i]);
}
}
return res;
}
// 方法2:
function nonDup(arr){
var res = new Set(arr);
return [...res];
}
// 方法3:
function nonDup(arr){
return arr.filter((elem, index) => {
return index === arr.indexOf(elem);
});
}
// 方法4:
Array.prototype.uniq = function () {
var hasNaN = false;
for(var i = 0; i < this.length; i++){
if(this[i] !== this[i]) hasNaN = true;
for(var j = i+1; j < this.length;){
if(this[i] === this[j] ||(hasNaN && this[j] !== this[j])){
this.splice(j,1);
}else{
j++;
}
}
}
return this;
}
複製程式碼
2.實現滑鼠滑過頭像顯示簡介
<!DOCTYPE html>
<html>
<head>
<style>
.div1{
width:100px;
height:100px;
background-color:red;
border-radius: 50px;
}
.div2{
width:100px;
height:200px;
margin-top: 10px;
background-color:black;
display: none;
}
</style>
</head>
<body>
<div class='div1'></div>
<div class='div2'></div>
<script type="text/javascript">
var d1 = document.getElementsByClassName('div1')[0];
var d2 = document.getElementsByClassName('div2')[0];
var timer;
d1.addEventListener('mouseenter',function(){
timer = window.setTimeout(function(){d2.style.display="block"},300);
})
d1.addEventListener('mouseout',function(){
window.clearTimeout(timer);
d2.style.display="none";
})
</script>
</body>
</html>
複製程式碼