1. 程式人生 > >2018年最新最基本的JavaScript面試問題及答案

2018年最新最基本的JavaScript面試問題及答案

我有一個很好的Java學習交流群,裡面有很多技術大牛,如果你感興趣的話可以加群(471948038)一起交流學習。

1.使用 typeof bar === "object" 來確定 bar 是否是物件的潛在陷阱是什麼?如何避免這個陷阱?

儘管 typeof bar === "object" 是檢查 bar 是否物件的可靠方法,令人驚訝的是在JavaScript中 null 也被認為是物件!

因此,令大多數開發人員驚訝的是,下面的程式碼將輸出 true (而不是false) 到控制檯:

var bar = null;
console.log(typeof bar === "object");  // logs true!

只要清楚這一點,同時檢查 bar 是否為 null,就可以很容易地避免問題:

console.log((bar !== null) && (typeof bar === "object"));  // logs false

要答全問題,還有其他兩件事情值得注意:

首先,上述解決方案將返回 false,當 bar 是一個函式的時候。在大多數情況下,這是期望行為,但當你也想對函式返回 true 的話,你可以修改上面的解決方案為:

console.log((bar !== null) && ((typeof bar === "object") || (typeof bar === "function"
)));

第二,上述解決方案將返回 true,當 bar 是一個數組(例如,當 var bar = [];)的時候。在大多數情況下,這是期望行為,因為陣列是真正的物件,但當你也想對陣列返回 false 時,你可以修改上面的解決方案為:

console.log((bar !== null) && (typeof bar === "object") && (toString.call(bar) !== "[object Array]"));

或者,如果你使用jQuery的話:

console.log((bar !== null) && (typeof bar === "object"
) && (! $.isArray(bar)));

2.下面的程式碼將輸出什麼到控制檯,為什麼?

(function(){
  var a = b = 3;
})();

console.log("a defined? " + (typeof a !== 'undefined'));
console.log("b defined? " + (typeof b !== 'undefined'));

由於 a 和 b 都定義在函式的封閉範圍內,並且都始於 var關鍵字,大多數JavaScript開發人員期望 typeof a 和 typeof b 在上面的例子中都是undefined。

然而,事實並非如此。這裡的問題是,大多數開發人員將語句 var a = b = 3; 錯誤地理解為是以下宣告的簡寫:

var b = 3;
var a = b;

但事實上,var a = b = 3; 實際是以下宣告的簡寫:

b = 3;
var a = b;

因此(如果你不使用嚴格模式的話),該程式碼段的輸出是:

a defined? false
b defined? true

但是, b 如何才能被定義在封閉函式的範圍之外呢?是的,既然語句 var a = b = 3; 是語句 b = 3; 和 var a = b;的簡寫, b 最終成為了一個全域性變數(因為它沒有字首 var 關鍵字),因此仍然在範圍內甚至封閉函式之外。

需要注意的是,在嚴格模式下(即使用 use strict),語句var a = b = 3; 將生成ReferenceError: b is not defined的執行時錯誤,從而避免任何否則可能會導致的headfakes /bug。 (還是你為什麼應該理所當然地在程式碼中使用 use strict 的最好例子!)

3.下面的程式碼將輸出什麼到控制檯,為什麼?

var myObject = {
    foo: "bar",
    func: function() {
        var self = this;
        console.log("outer func:  this.foo = " + this.foo);
        console.log("outer func:  self.foo = " + self.foo);
        (function() {
            console.log("inner func:  this.foo = " + this.foo);
            console.log("inner func:  self.foo = " + self.foo);
        }());
    }
};
myObject.func();

上面的程式碼將輸出以下內容到控制檯:

outer func:  this.foo = bar
outer func:  self.foo = bar
inner func:  this.foo = undefined
inner func:  self.foo = bar

在外部函式中, this 和self 兩者都指向了 myObject,因此兩者都可以正確地引用和訪問 foo

在內部函式中, this 不再指向 myObject。其結果是,this.foo 沒有在內部函式中被定義,相反,指向到本地的變數self 保持在範圍內,並且可以訪問。 (在ECMA 5之前,在內部函式中的this 將指向全域性的 window 物件;反之,因為作為ECMA 5,內部函式中的功能this 是未定義的。)

4.封裝JavaScript原始檔的全部內容到一個函式塊有什麼意義及理由?

這是一個越來越普遍的做法,被許多流行的JavaScript庫(jQuery,Node.js等)採用。這種技術建立了一個圍繞檔案全部內容的閉包,也許是最重要的是,建立了一個私有的名稱空間,從而有助於避免不同JavaScript模組和庫之間潛在的名稱衝突。

這種技術的另一個特點是,允許一個易於引用的(假設更短的)別名用於全域性變數。這通常用於,例如,jQuery外掛中。jQuery允許你使用jQuery.noConflict(),來禁用 $ 引用到jQuery名稱空間。在完成這項工作之後,你的程式碼仍然可以使用$ 利用這種閉包技術,如下所示:

(function($) { /* jQuery plugin code referencing $ */ } )(jQuery);

5.在JavaScript原始檔的開頭包含 use strict 有什麼意義和好處?

對於這個問題,既簡要又最重要的答案是,use strict 是一種在JavaScript程式碼執行時自動實行更嚴格解析和錯誤處理的方法。那些被忽略或默默失敗了的程式碼錯誤,會產生錯誤或丟擲異常。通常而言,這是一個很好的做法。

嚴格模式的一些主要優點包括:

  • 使除錯更加容易。那些被忽略或默默失敗了的程式碼錯誤,會產生錯誤或丟擲異常,因此儘早提醒你程式碼中的問題,你才能更快地指引到它們的原始碼。
  • 防止意外的全域性變數。如果沒有嚴格模式,將值分配給一個未宣告的變數會自動建立該名稱的全域性變數。這是JavaScript中最常見的錯誤之一。在嚴格模式下,這樣做的話會丟擲錯誤。
  • 消除 this 強制。如果沒有嚴格模式,引用null或未定義的值到 this 值會自動強制到全域性變數。這可能會導致許多令人頭痛的問題和讓人恨不得拔自己頭髮的bug。在嚴格模式下,引用 null或未定義的 this 值會丟擲錯誤。
  • 不允許重複的屬性名稱或引數值。當檢測到物件(例如,var object = {foo: "bar", foo: "baz"};)中重複命名的屬性,或檢測到函式中(例如,function foo(val1, val2, val1){})重複命名的引數時,嚴格模式會丟擲錯誤,因此捕捉幾乎可以肯定是程式碼中的bug可以避免浪費大量的跟蹤時間。
  • 使eval() 更安全。在嚴格模式和非嚴格模式下,eval() 的行為方式有所不同。最顯而易見的是,在嚴格模式下,變數和宣告在 eval() 語句內部的函式不會在包含範圍內建立(它們會在非嚴格模式下的包含範圍中被建立,這也是一個常見的問題源)。
  • 在 delete使用無效時丟擲錯誤。delete操作符(用於從物件中刪除屬性)不能用在物件不可配置的屬性上。當試圖刪除一個不可配置的屬性時,非嚴格程式碼將默默地失敗,而嚴格模式將在這樣的情況下丟擲異常。

6.考慮以下兩個函式。它們會返回相同的東西嗎? 為什麼相同或為什麼不相同?

function foo1()
{
  return {
      bar: "hello"
  };
}

function foo2()
{
  return
  {
      bar: "hello"
  };
}

出人意料的是,這兩個函式返回的內容並不相同。更確切地說是:

console.log("foo1 returns:");
console.log(foo1());
console.log("foo2 returns:");
console.log(foo2());

將產生:

foo1 returns:
Object {bar: "hello"}
foo2 returns:
undefined

這不僅是令人驚訝,而且特別讓人困惑的是, foo2()返回undefined卻沒有任何錯誤丟擲。

原因與這樣一個事實有關,即分號在JavaScript中是一個可選項(儘管省略它們通常是非常糟糕的形式)。其結果就是,當碰到 foo2()中包含 return語句的程式碼行(程式碼行上沒有其他任何程式碼),分號會立即自動插入到返回語句之後。

也不會丟擲錯誤,因為程式碼的其餘部分是完全有效的,即使它沒有得到呼叫或做任何事情(相當於它就是是一個未使用的程式碼塊,定義了等同於字串 "hello"的屬性 bar)。

這種行為也支援放置左括號於JavaScript程式碼行的末尾,而不是新程式碼行開頭的約定。正如這裡所示,這不僅僅只是JavaScript中的一個風格偏好。

7. NaN 是什麼?它的型別是什麼?你如何可靠地測試一個值是否等於 NaN ?

NaN 屬性代表一個“不是數字”的值。這個特殊的值是因為運算不能執行而導致的,不能執行的原因要麼是因為其中的運算物件之一非數字(例如, "abc" / 4),要麼是因為運算的結果非數字(例如,除數為零)。

雖然這看上去很簡單,但 NaN 有一些令人驚訝的特點,如果你不知道它們的話,可能會導致令人頭痛的bug。

首先,雖然 NaN 意味著“不是數字”,但是它的型別,不管你信不信,是 Number

console.log(typeof NaN === "number");  // logs "true"

此外, NaN 和任何東西比較——甚至是它自己本身!——結果是false:

console.log(NaN === NaN);  // logs "false"

一種半可靠的方法來測試一個數字是否等於 NaN,是使用內建函式 isNaN(),但即使使用 isNaN() 依然並非是一個完美的解決方案。

一個更好的解決辦法是使用 value !== value,如果值等於NaN,只會產生true。另外,ES6提供了一個新的 Number.isNaN() 函式,這是一個不同的函式,並且比老的全域性 isNaN() 函式更可靠。

8.下列程式碼將輸出什麼?並解釋原因。

console.log(0.1 + 0.2);
console.log(0.1 + 0.2 == 0.3);

一個稍微有點程式設計基礎的回答是:“你不能確定。可能會輸出“0.3”和“true”,也可能不會。JavaScript中的數字和浮點精度的處理相同,因此,可能不會總是產生預期的結果。“

以上所提供的例子就是一個演示了這個問題的典型例子。但出人意料的是,它會輸出:

0.30000000000000004
false

9.討論寫函式 isInteger(x) 的可能方法,用於確定x是否是整數。

這可能聽起來是小菜一碟,但事實上,這很瑣碎,因為ECMAScript 6引入了一個新的正以此為目的 Number.isInteger() 函式。然而,之前的ECMAScript 6,會更復雜一點,因為沒有提供類似的 Number.isInteger() 方法。

問題是,在ECMAScript規格說明中,整數只概念上存在:即,數字值總是儲存為浮點值。

考慮到這一點,最簡單又最乾淨的ECMAScript6之前的解決方法(同時也非常穩健地返回 false ,即使一個非數字的值,如字串或 null ,被傳遞給函式)如下:

function isInteger(x) { return (x^0) === x; }

下面的解決方法也是可行的,雖然不如上面那個方法優雅:

function isInteger(x) { return Math.round(x) === x; }

請注意 Math.ceil() 和 Math.floor() 在上面的實現中等同於 Math.round()

或:

function isInteger(x) { return (typeof x === 'number') && (x % 1 === 0);

相當普遍的一個不正確的解決方案是:

function isInteger(x) { return parseInt(x, 10) === x; }

雖然這個以 parseInt函式為基礎的方法在 x 取許多值時都能工作良好,但一旦 x 取值相當大的時候,就會無法正常工作。問題在於 parseInt() 在解析數字之前強制其第一個引數到字串。因此,一旦數目變得足夠大,它的字串就會表達為指數形式(例如, 1e+21)。因此,parseInt() 函式就會去解析 1e+21,但當到達 e字串的時候,就會停止解析,因此只會返回值 1。注意:

> String(1000000000000000000000)
'1e+21'

> parseInt(1000000000000000000000, 10)
1

> parseInt(1000000000000000000000, 10) === 1000000000000000000000
false

10.下列程式碼行1-4如何排序,使之能夠在執行程式碼時輸出到控制檯? 為什麼?

(function() {
    console.log(1); 
    setTimeout(function(){console.log(2)}, 1000); 
    setTimeout(function(){console.log(3)}, 0); 
    console.log(4);
})();

序號如下:

1
4
3
2

讓我們先來解釋比較明顯而易見的那部分:

  • 1 和 4之所以放在前面,是因為它們是通過簡單呼叫 console.log() 而沒有任何延遲輸出的
  • 2 之所以放在 3的後面,是因為 2 是延遲了1000毫秒(即,1秒)之後輸出的,而 3 是延遲了0毫秒之後輸出的。

好的。但是,既然 3 是0毫秒延遲之後輸出的,那麼是否意味著它是立即輸出的呢?如果是的話,那麼它是不是應該在 4 之前輸出,既然 4 是在第二行輸出的?

要回答這個問題,你需要正確理解JavaScript的事件和時間設定。

瀏覽器有一個事件迴圈,會檢查事件佇列和處理未完成的事件。例如,如果時間發生在後臺(例如,指令碼的 onload 事件)時,瀏覽器正忙(例如,處理一個 onclick),那麼事件會新增到佇列中。當onclick處理程式完成後,檢查佇列,然後處理該事件(例如,執行 onload 指令碼)。

同樣的, setTimeout() 也會把其引用的函式的執行放到事件佇列中,如果瀏覽器正忙的話。

setTimeout()的第二個引數為0的時候,它的意思是“儘快”執行指定的函式。具體而言,函式的執行會放置在事件佇列的下一個計時器開始。但是請注意,這不是立即執行:函式不會被執行除非下一個計時器開始。這就是為什麼在上述的例子中,呼叫 console.log(4) 發生在呼叫 console.log(3) 之前(因為呼叫 console.log(3) 是通過setTimeout被呼叫的,因此會稍微延遲)。

11.寫一個簡單的函式(少於80個字元),要求返回一個布林值指明字串是否為迴文結構。

下面這個函式在 str 是迴文結構的時候返回true,否則,返回false。

function isPalindrome(str) {
    str = str.replace(/\W/g, '').toLowerCase();
    return (str == str.split('').reverse().join(''));
}

例如:

console.log(isPalindrome("level"));                   // logs 'true'
console.log(isPalindrome("levels"));                  // logs 'false'
console.log(isPalindrome("A car, a man, a maraca"));  // logs 'true'

12.寫一個 sum方法,在使用下面任一語法呼叫時,都可以正常工作。

console.log(sum(2,3));   // Outputs 5
console.log(sum(2)(3));  // Outputs 5

(至少)有兩種方法可以做到:

方法1

function sum(x) {
  if (arguments.length == 2) {
    return arguments[0] + arguments[1];
  } else {
    return function(y) { return x + y; };
  }
}

在JavaScript中,函式可以提供到 arguments 物件的訪問,arguments 物件提供傳遞到函式的實際引數的訪問。這使我們能夠使用 length 屬性來確定在執行時傳遞給函式的引數數量。

如果傳遞兩個引數,那麼只需加在一起,並返回。

否則,我們假設它被以 sum(2)(3)這樣的形式呼叫,所以我們返回一個匿名函式,這個匿名函式合併了傳遞到 sum()的引數和傳遞給匿名函式的引數。

方法2

function sum(x, y) {
  if (y !== undefined) {
    return x + y;
  } else {
    return function(y) { return x + y; };
  }
}

當呼叫一個函式的時候,JavaScript不要求引數的數目匹配函式定義中的引數數量。如果傳遞的引數數量大於函式定義中引數數量,那麼多餘引數將簡單地被忽略。另一方面,如果傳遞的引數數量小於函式定義中的引數數量,那麼缺少的引數在函式中被引用時將會給一個 undefined值。所以,在上面的例子中,簡單地檢查第2個引數是否未定義,就可以相應地確定函式被呼叫以及進行的方式。

13.請看下面的程式碼片段:

for (var i = 0; i < 5; i++) {
  var btn = document.createElement('button');
  btn.appendChild(document.createTextNode('Button ' + i));
  btn.addEventListener('click', function(){ console.log(i); });
  document.body.appendChild(btn);
}

(a)當用戶點選“Button 4”的時候會輸出什麼到控制檯,為什麼?(b)提供一個或多個備用的可按預期工作的實現方案。

(a)無論使用者點選什麼按鈕,數字5將總會輸出到控制檯。這是因為,當 onclick 方法被呼叫(對於任何按鈕)的時候, for 迴圈已經結束,變數 i 已經獲得了5的值。(面試者如果能夠談一談有關如何執行上下文,可變物件,啟用物件和內部“範圍”屬性貢有助於閉包行為,則可以加分)。

(b)要讓程式碼工作的關鍵是,通過傳遞到一個新建立的函式物件,在每次傳遞通過 for 迴圈時,捕捉到 i 值。下面是三種可能實現的方法:

for (var i = 0; i < 5; i++) {
  var btn = document.createElement('button');
  btn.appendChild(document.createTextNode('Button ' + i));
  btn.addEventListener('click', (function(i) {
    return function() { console.log(i); };
  })(i));
  document.body.appendChild(btn);
}

或者,你可以封裝全部呼叫到在新匿名函式中的 btn.addEventListener :

for (var i = 0; i < 5; i++) {
  var btn = document.createElement('button');
  btn.appendChild(document.createTextNode('Button ' + i));
  (function (i) {
    btn.addEventListener('click', function() { console.log(i); });
  })(i);
  document.body.appendChild(btn);
}

也可以呼叫陣列物件的本地 forEach 方法來替代 for 迴圈:

['a', 'b', 'c', 'd', 'e'].forEach(function (value, i) {
  var btn = document.createElement('button');
  btn.appendChild(document.createTextNode('Button ' + i));
  btn.addEventListener('click', function() { console.log(i); });
  document.body.appendChild(btn);
});

14.下面的程式碼將輸出什麼到控制檯,為什麼?

var arr1 = "john".split('');
var arr2 = arr1.reverse();
var arr3 = "jones".split('');
arr2.push(arr3);
console.log("array 1: length=" + arr1.length + " last=" + arr1.slice(-1));

            
           

相關推薦

2018最新基本JavaScript面試問題答案

我有一個很好的Java學習交流群,裡面有很多技術大牛,如果你感興趣的話可以加群(471948038)一起交流學習。1.使用 typeof bar === "object" 來確定 bar 是否是物件的潛在陷阱是什麼?如何避免這個陷阱?儘管 typeof bar === "ob

2018最新】 iOS面試題答案

(1)#import指令是Object-C針對@include的改進版本,能確保引用的檔案只會被引用一次,不會陷入遞迴包含的問題中;(2)@import與@class的區別:    #import會鏈入該標頭檔案的全部資訊,包括實體變數和方法等;二@class只是告訴編譯器,其後面宣告的名稱是類的名稱,至於這

2018UI設計師經典面試題答案總結

2018年馬上就要結束了,給大家總結了一些UI設計出現比較多的面試題,很多時候我們有足夠的技術,但是可能就斷送在了面試上,大家如果去面試,可以多瞭解看看以下的這些UI設計師經典面試題及答案,畢竟知己知彼才能更好的發揮自己的特長,提高面試的機率嘛。 1、推動一個專案的時間要多久?舉個例子? 答案:專

2018web前端經典面試題答案

    javascript: JavaScript中如何檢測一個變數是一個String型別?請寫出函式實現方法1、function isString(obj){ return typeof(obj) === "string"? true: false; //

2018最新 Vultr 註冊 VPS 購買教程

硬盤 art 至少 而且 包含 字母 郵箱 sts 器) 小編早就想搞一臺VPS玩玩,正好借著測試免流的機會買了一臺vultr的VPS,今天就給大家介紹一下如何購買Vultr的VPS,網上也有教程不過都有點太早了,而且不夠全面。以下我將詳細介紹如何註冊Vultr、註冊Pay

2018最新榜單!全球快Top10超級計算機花落誰家?!

數量 bytes ces 它的 f11 可靠 融合 性價比 小時 美國重登500強榜首,這是一份每年更新兩次的世界最快超級計算機排行榜。自2012年以來,還沒有一個美國系統排在榜單的首位。前兩年,中國的“神威·太湖一號”名列榜首。在此之前的三年裏,中國的“天河二號”系統一直

2018經典的26個JavaScript面試題和答案

根據 Stack Overflow 的 2018 年度調查,JavaScript 連續六年成為最常用的程式語言。所以我們必須面對這樣的現實,JavaScript 已經成為全棧開發技能的基石,在全棧開發面試中都會不可避免地涉及到與 JavaScript 有關的問題。FullStack.Cafe 彙編了

2018最新Java面試題答案整理(持續完善中…)

下列面試題都是在網上收集的,本人抱著學習的態度找了下參考答案,有不足的地方還請指正 基礎篇 基本功 面向物件特徵 封裝,繼承,多型和抽象 封裝 封裝給物件提供了隱藏內部特性和行為的能力。物件提供一些能被其他物件訪問的方法來改 變它內部的資料。在 Ja

20183月php開發面試最新快報(鏈家+一下科技+新浪+自如+百度)-熱氣騰騰[題目+答案]

===========================寫在前面:這兩天面了幾家,趕緊趁著還記得,把面試題記錄下來,以供參考。鏈家:注重基礎和底層原理,還有程式碼的異常處理,摳細節。不過比較人性化,注重持久化發展,上下班不用打卡,活幹完了6,7想撤就可以撤,好任性的有沒有。然後

Java面試----2018最新Struts2面試題

1、描述Struts2的工作原理答:客戶端傳送請求--》請求經過一系列過濾器--》FilterDispatcher通過ActionMapper來決定這個Request需要呼叫哪個Action --》FilterDispatcher把請求的處理交給ActionProxy--》通

2018最新面試經驗總結

核心:前期準備,簡歷,自信,實力,態度,語言表達 1.簡歷製作 簡歷要有特點,將部落格,GitHub,自己的作品連結,還有電子版本的簡歷連結直接寫上去,很重要。 特長描述,工作經歷,崗位職責,技能描述,專案經歷描述,每一個專案都要能講的非常的清楚,佈局合理,邏輯清晰

Ununto16.04版本Docker的安裝2018最新版本

sta mage drive pri brush stream down 和我 sport 最近參照網上教程安裝ubuntu的Docker怎麽都不成功,我最後參照官方文檔成功安裝 https://docs.docker.com/engine/installation/lin

2018最新Python書單

自動 aca lin python語言 許可證 數字信號處理 軟件 業界 高效 Python這個“無所不在”的編程語言,學會它,薪資高到沒朋友。2018年初這些Python新書值得關註,正要踏入Python學堂的,一定要收藏。號外,異步社區招募書評人,如果你意向加入,在微信

2018最新Linux入門學習路線圖

Linux入門路線圖進入全新的2018年之後,感覺Linux入門學習的路線也應該進行一下更新了。過去一年裏Linux版本更新過不少版本,而且很多新技術沖擊了應用市場,不及時更新一下很可能跟用人需求南轅北轍。正好拿到一份不錯的入門路線圖,所以就先發出來給大家做個參考。2018年最新Linux入門學習路線圖

2018最新Linux雲計算入門學習路線圖

Linux Linux運維 近年來,Linux在技術行業裏的重要性越來越高,成為了IT從業者的必備技能之一,據統計,Linux相關崗位增長達到了驚人的24%。市場需求擴大後Linux技術人員的薪資也一路上升,1-3年Linux運維工程師的平均薪資上升了13%。因為幾年來很多人都選擇轉行從事Linux運

2018最新手把手教你搭建中小型互聯網公司後臺服務架構與運維架構

前端 詳細 token 使用詳解 restful jedis 以及 tom mvc 本課程主要是針對如何從無到有搭建中小型互聯網公司後臺服務架構和運維架構的課程,課程所涉及的內容均是當前應用最廣泛的技術和工具。本課程所講解的技術體系已經在多個中小型互聯網公司中實戰運行使用,

2018NGINX新版高級視頻教程

post img blog font inf src size idt -s 2018年NGINX最新版高級視頻教程,想要的聯系我,QQ:1844912514 2018年NGINX最新版高級視頻教程

2018最新大數據24期實戰項目 9天 附課件源碼

媒體 truct 展示 HP 面試問題 for php 重要 業務 課程目錄:第一天:01.傳統廣告回顧02.幾個問題思考03.廣告的表現形式04.名詞解釋05.DSP原理圖06.DSP業務流程07.DMP項目背景08.DMP業務流程----重要09.日誌格式介紹10.需求

2018最新整理ios APP審核被拒的常見原因

位置 cati bsp 支持 htm 三方庫 采集 導航 例如 蘋果APP的審核是一道大難關,遇見被拒是很正常的事情,如果被拒就根據反饋問題,和下面提供的思路去尋求解決方案。 蘋果審核大體分為三部分,預審、機審和人工審核。 ipa包上傳後首先進入的是預審,會被

2018最新微信小程序開發 零基礎

頁面布局 col In cnblogs 開發 www IT 什麽是 http 第一季 1節 什麽是微信小程序2節 計算器實戰頁面布局3節 計算器實戰業務實現4節 實戰開發之天氣APP5節 天氣App頁面布局 第二季2018年最新微信小程序開發 零基礎