斌斌學院(js筆記)

在JavaScript中,一切都是對象。

element=document.querySelector(selectors)

使用深度優先遍歷,element是一個DOM元素,selectors是一個字符串,包含一個或者多個CSS選擇器。如果沒有找到匹配元素,則返回null,如果找到國歌匹配元素,則返回找到的第一個匹配到的元素。傳遞給 querySelector的字符串參數必須符合CSS語法。

document.getElementById(id);
document.getElementsByTag(classname);

通過id獲取元素和通過class獲取元素

Object.style.property;
Object.className;
Object.disabled;

改變元素樣式、獲取元素classname和設置元素disabled屬性

4.js代碼錯誤一般包括兩種,語法錯誤和邏輯錯誤。

5.你可以先初始化變量然后再聲明一個變量,這樣與先聲明變量再初始化是一樣的。這叫做變量提升(var hoisting)。

6.變量命名習慣

An aside on variable naming rules

You can call a variable pretty much anything you like, but there are limitations. Generally you should stick to just using Latin characters (0-9, a-z, A-Z) and the underscore character.

You shouldn't use other characters because they may cause errors or be hard to understand by an international audience.

Don't use underscores at the start of variable names — this is used in certain JavaScript constructs to mean specific things, so may get confusing.

Don't use numbers at the start of variables. This isn't allowed and will cause an error.

A safe convention to stick to is so-called "lower camel case", where you stick together multiple words, using lower case for the whole first word and then capitalize subsequent words. We've been using this for our variable names in the article so far.

Make variable names intuitive, so they describe the data they contain. Don't just use single letters/numbers, or big long phrases.

Variables are case sensitive — so myage is a different variable to myAge.

One last point — you also need to avoid using JavaScript reserved words as your variable names — by this, we mean the words that make up the actual syntax of JavaScript! So you can't use words like var, function, let, and for as variable names. Browsers will recognize them as different code items, and so you'll get errors.

7.Variable types

There are a few different types of data we can store in variables (data types). In this section we'll describe these in brief, then in future articles you'll learn about them in more detail.

So far we've looked at the first two, but there are others.

Numbers

You can store numbers in variables, be those whole numbers like 30 (also called integers) or decimal numbers like 2.456 (also called floats or floating point numbers). JavaScript doesn't have different data types for different number types, like some programming languages do. When you give a variable a number value, you don't include quotes:

var myAge = 22;

Strings

Strings are pieces of text. When you give a variable a string value, you need to wrap it in single or double quote marks, otherwise JavaScript will try to intepret it as another variable name.

var dophinGoodbye="so long and thanks for all the fish";

Booleans

Booleans are true/false values — they can have two values, true or false. These are generally used to test a condition, after which code is run as appropriate. So for example, a simple case would be:

var iAmAlive= true;

Whereas in reality it would be used more like this:

var test= 6<3;

This is using the "less than" operator (<) to test whether 6 is less than 3. As you might expect, it will return false, because 6 is not less than 3! You will learn a lot more about such operators later on in the course.

Arrays

An array is a single object that contains multiple values enclosed in square brackets and separated by commas. Try entering the following lines into your console:

var myNameArray=['Chris','Bob','Jim];
var myNumberArray=[10,15,45];

Arrays

An array is a single object that contains multiple values enclosed in square brackets and separated by commas. Try entering the following lines into your console:

myNameArray[0];
myNumberArray[2];

The square brackets here contain an index value that specifies the position of the value you want returned. You might have noticed that computers count from 0, instead of 1 like us humans do.

You'll learn a lot more about arrays in a future article.

Objects

In programming, an object is a structure of code that models a real life object. You can have a simple object that represents a car park and contains information about its width and length, or you could have an object that represents a person, and contains data about their name, height, weight, what language they speak, how to say hello to them, and more.

Try entering the following line into you console:

var dog={name:'Spot',breed:'Dalmatian'};

To retrieve the information stored in the object, you can use the following syntax:

dog.name

We won't be looking at objects any more for now — you can learn more about those in a future module.

8.Loose typing

JavaScript is a "loosely typed language", which means that, unlike some other languages, you don't need to specify what data type a variable will contain (e.g. number? string?).

For example if you declare a variable and give it a value with quotes round it, the browser will know it is a string:

var myString='hello';

It will still be a string, even if it contains numbers, so be careful:

var myNumber='500';
typeof(myNumber);
myNumber=500;
typeof(myNumber);

Try entering the four lines above into your console one by one, and see what the results are (don't type in the comments). You'll notice that we are using a special function called typeof() — this returns the data type of the variable you pass to it. The first time it is called in the above sequence, it should return string, as at that point the myNumber variable contains a string, '500'. Have a look and see what it returns the second time you call it.

8.數字類型

整數: 就是整數,例如 10, 400, 或者 -5.
浮點數: (浮點) 有小數點或小數位,例如 12.5,和 56.7786543。
雙精度: 雙精度是一種特定類型的浮點數,它們具有比標準浮點數更高的精度(這意味著它們精確到更大的小數位數)。

9.Comparison operators

You may see some people using == and != in their code for equality and non-equality — these are valid operators in JavaScript, but they differ from ===/!== — the former test whether the values are the same, but the datatype can be different, whereas the latter strict versions test if the value and the dataype are the same. The strict versions tend to result in less errors going undetected, so we are recommending that you use those.

10.Escaping characters in a string

To fix our previous problem code line, we need to escape the problem quote mark. Escaping characters means that we do something to them to make sure they are recognized as text, not part of the code. In JavaScript, we do this by putting a backslash just before the character. Try this:

var bigMouth='I\'ve got no right to take my place...';
var myNum=Number(myString);
var string=myNum.toString();

利用以上方法來進行字符串和數字類型的轉換

12.Useful string methods

//在瀏覽器開發者控制臺輸入以下內容
var browser='mozilla';
browser.length;
browser[0];
browser.indexOf('zilla');
browser.slice(0,3);
browser.slice(2);
//slice的第二個值是可選的,如果輸入的值是負數,那么代表length+index。
var rad="I Love You";
rad.toUpperCase();
rad.toLowerCase();
browser.replace('moz','van');

13.Some useful array methods

var myData = 'Manchester,London,Liverpool,Birmingham,Leeds,Carlisle';
var myArray = myData.split(',');
myArray;
var myNewString = myArray.join(',');
myNewString;
myArray.push('Cardiff');
myArray;
myArray.push('Bradford', 'Brighton');
myArray;
var newLength = myArray.push('Bristol');
//array.push()會產生一個返回值,返回數組的長度
myArray;
newLength;
myArray.pop();
var removedItem = myArray.pop();
//array.pop返回刪除的值
myArray;
removedItem;
myArray.unshift('Edinburgh');//返回數組長度
myArray;
var removedItem = myArray.shift();//返回刪除的值
myArray;
removedItem;

14.conditional statement

Any value that is not false, undefined, null, 0, NaN, or an empty string ('') actually returns true when tested as a conditional statement.

switch(expression){
    case choice1:
        run this code
        break;
    case choice2:
        run this code
        break;

//include as many cases as you like

    default:
        actually,just run this code

15.Loop statement

iteration

16.Function

Functions versus methods

One thing we need to clear up before we move on — technically speaking, built in browser functions are not functions — they are methods.

The distinction is that methods are functions defined inside objects. Built-in browser functions (methods) and variables (which are called properties) are stored inside structured objects, to make the code more efficient and easier to handle.

Anonymous function

You can also assign an anonymous function to be the value of a variable, for example:

var myGreeting = function() {
  alert('hello');
}

myGreeting();

Note: The same scoping rules do not apply to loop (e.g. for() { ... }) and conditional blocks (e.g. if() { ... }) — they look very similar, but they are not the same thing! Take care not to get these confused.

17.Return Value

利用isNaN()來測試是否是一個數字。

18.Introduction to Events

Note that event handlers are sometimes called event listeners — they are pretty much interchangeable for our purposes, although strictly speaking they work together. The listener listens out for the event happening, and the handler is the code that is run in response to it happening.

Note: It is important to note that web events are not part of the core JavaScript language — they are defined as part of the JavaScript APIs built into the browser.

onclick
onfocus
onblur
ondblcilck
onkeypress
onkeyup
onkeydown
onmouseover
onmouseout

window.onkeypress, window.onkeydown, window.onkeyup — The color will change when a key is pressed on the keyboard. keypress refers to a general press (button down and then up), while keydown and keyup refer to just the key down and key up parts of the keystroke, respectively. Note that it doesn't work if you try to register this event handler on the button itself — we've had to register it on the button itself — we've had to register it on the window object, which represents the entire browser window.

<button onclick="bgChange()">Press me</button>

You'll find HTML attribute equivalents for many of the event handler properties; however, you shouldn't use these — they are considered bad practice. It might seem easy to use an event handler attribute if you are just doing something really quick, but they very quickly become unmanageable and inefficient.

The newest type of event mechanism is defined in the Document Object Model (DOM) Level 2 Events Specification, which provides browsers with a new function — addEventListener(). This functions in a similar way to the event handler properties, but the syntax is obviously different. We could rewrite our random color example to look like this:

var btn = document.querySelector('button');

function bgChange() {
  var rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
  document.body.style.backgroundColor = rndCol;
}   

btn.addEventListener('click', bgChange);

Note that it is perfectly appropriate to put all the code inside the addEventListener() function, in an anonymous function, like this:

btn.addEventListener('click', function() {
  var rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
  document.body.style.backgroundColor = rndCol;
});

This mechanism has some advantages over the older mechanisms discussed earlier. For a start, there is a counterpart function, removeEventListener()
, which removes a previously added listener. For example, this would remove the listener set in the first code block in this section:

btn.removeEventListener('click', bgChange);

Second, you can also register multiple handlers for the same listener. The following two handlers would not be applied:

myElement.onclick = functionA;
myElement.onclick = functionB;

As the second line would overwrite the first value of onclick set. This would work, however:

myElement.addEventListener('click', functionA);
myElement.addEventListener('click', functionB);

Both functions would now run when the element is clicked.

What mechanism should I use?

Of the three mechanisms, you definitely shouldn't use the HTML event handler attributes — these are outdated, and bad practice, as mentioned above.

The other two are relatively interchangeable, at least for simple uses:

Event handler properties have less power and options, but better cross browser compatibility (being supported as far back as Internet Explorer 8). You should probably start with these as you are learning.

DOM Level 2 Events (addEventListener(), etc.) are more powerful, but can also become more complex and are less well supported (supported as far back as Internet Explorer 9). You should also experiment with these, and aim to use them where possible.

The main advantages of the third mechanism are that you can remove event handler code if needed, using removeEventListener(), and you can add multiple listeners of the same type to elements if required.

Event objects

Sometimes inside an event handler function you might see a parameter specified with a name such as event, evt, or simply e. This is called the event object, and it is automatically passed to event handlers to provide extra features and information. For example, let's rewrite our random color example again slightly:

function bgChange(e) {
  var rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
  e.target.style.backgroundColor = rndCol;
  console.log(e);
}  

btn.addEventListener('click', bgChange);

Here you can see that we are including an event object, e, in the function, and in the function setting a background color style on e.target — which is the button itself. The target property of the event object is always a reference to the element that the event has just occurred upon. So in this example we are setting a random background color on the button, not the page.

Preventing default behaviour

The trouble comes when the user has not submitted the data correctly — as a developer, you'll want to stop the submission to the server and give them an error message telling them what's wrong and what needs to be done to put things right. Some browsers support automatic form data validation features, but since many don't, you are advised to not rely on those, and implement your own validation checks. Let's look at a simple example.

<form>
  <div>
    <label for="fname">First name: </label>
    <input id="fname" type="text">
  </div>
  <div>
    <label for="lname">Last name: </label>
    <input id="lname" type="text">
  </div>
  <div>
     <input id="submit" type="submit">
  </div>
</form>
<p></p>

var form = document.querySelector('form');
var fname = document.getElementById('fname');
var lname = document.getElementById('lname');
var submit = document.getElementById('submit');
var para = document.querySelector('p');

form.onsubmit = function(e) {
  if (fname.value === '' || lname.value === '') {
    e.preventDefault();
    para.textContent = 'You need to fill in both names!';
  }
}

Event bubbling and capture

The final subject to cover here is something that you'll not come across often, but it can be a real pain if you don't understand it. Event bubbling and capture are two mechanisms that describe what happens when two handlers of the same event type are activated on one element. Let's look at an example to make this easier — open up the show-video-box.html example in a new tab (and the source code in another tab.) It is also available live below:

This is a pretty simple example that shows and hides a <div> with a <video> element inside it:

<button>Display video</button>

<div class="hidden">
  <video>
    <source src="rabbit320.mp4" type="video/mp4">
    <source src="rabbit320.webm" type="video/webm">
    <p>Your browser doesn't support HTML5 video. Here is a <a href="rabbit320.mp4">link to the video</a> instead.</p>
  </video>
</div>

When the <button> is clicked, the video is displayed, by changing the class attribute on the <div> from hidden to showing (the example's CSS contains these two classes, which position the box off the screen and on the screen, respectively):

btn.onclick = function() {
  videoBox.setAttribute('class', 'showing');
}

We then add a couple more onclick event handlers — the first one to the <div> and the second one to the <video>. The idea is that when the area of the <div> outside the video is clicked, the box should be hidden again; when the video itself is clicked, the video should start to play.

videoBox.onclick = function() {
  videoBox.setAttribute('class', 'hidden');
};

video.onclick = function() {
  video.play();
};

But there's a problem — currently when you click the video it starts to play, but it causes the <div> to also be hidden at the same time. This is because the video is inside the <div> — it is part of it — so clicking on the video actually runs both the above event handlers.

Bubbling and capturing explained

When an event is fired on an element that has parent elements (e.g. the <vedio> in our case), modern browsers run two different phases — the capturing phase and the bubbling phase. In the capturing phase:

The browser checks to see if the element's outer-most ancestor (<html>) has an onclick event handler registered on it in the capturing phase, and runs it if so.

Then it moves on to the next element inside <html> and does the same thing, then the next one, and so on until it reaches the element that was actually clicked on.

In the bubbling phase, the exact opposite occurs:

The browser checks to see if the element that was actually clicked on has an onclick event handler registered on it in the bubbling phase, and runs it if so.

Then it moves on to the next immediate ancestor element and does the same thing, then the next one, and so on until it reaches the <html> element.

In modern browsers, by default, all event handlers are registered in the bubbling phase. So in our current example, when you click the video, the click event bubbles from the <video> element outwards to the <html> element. Along the way:

It finds the video.onclick... handler and runs it, so the video first starts playing.

It then finds the videoBox.onclick... handler and runs it, so the video is hidden as well.

Fixing the problem with stopPropagation()

This is annoying behaviour, but there is a way to fix it! The standard event object has a function available on it called stopPropagation(),which when invoked on a handler's event object makes it so that handler is run, but the event doesn't bubble any further up the chain, so no more handlers will be run.

We can therefore fix our current problem by changing the second handler function in the previous code block to this:

video.onclick = function(e) {
  e.stopPropagation();
  video.play();
};

Event delegation

Event delegation

Bubbling also allows us to take advantage of event delegation — this concept relies on the fact that if you want some code to run when you click on any one of a large number of child elements, you can set the event listener on their parent and have the effect of the event listener bubble to each child, rather than having to set the event listener on every child individually.

A good example is a series of list items — if you want each one of them to pop up a message when clicked, you can can set the click event listener on the parent ul, and it will bubble to the list items.

19.對象

一個對象是一個包含相關資料和功能的集體(通常由一些變量和函數組成,我們稱之為對象里面的屬性和方法)。

對象的字面量(literal)——手動的寫出對象的內容來創建一個對象。

對象的名字表現為一個命名空間(namespace),它必須寫在第一位——當你想訪問對象內部的屬性或方法時,然后是一個點(.),緊接著是你想要訪問的項目,標識可以是簡單屬性的名字(name),或者是數組屬性的一個子元素,又或者是對象的方法調用。

person.age
person.interests[1]
person.bio()

可以用一個對象來做另一個對象成員的值。

外一種訪問屬性的方式是使用括號表示法(bracket notation),替代這樣的代碼

person.age
person.name.first
person['age']
person['name']['first']

這看起來很像訪問一個數組的元素,從根本上來說是一回事兒,你使用了關聯了值的名字,而不是索引去選擇元素。難怪對象有時被稱之為關聯數組(associative array)了——對象做了字符串到值的映射,而數組做的是數字到值的映射。

比如說,我們想讓用戶能夠在他們的數據里存儲自己定義的值類型,通過兩個input框來輸入成員的名字和值,通過以下代碼獲取用戶輸入的值:

var myDataName = nameInput.value
var myDataValue = nameValue.value

我們可以這樣把這個新的成員的名字和值加到person對象里:

person[myDataName] = myDataValue

為了測試這個功能,嘗試在你的代碼里添加以下幾行,就在person對象的右花括號的下面:

var myDataName = 'height'
var myDataValue = '1.75m'
person[myDataName] = myDataValue

現在,保存并刷新,在輸入框里輸入以下代碼:

person.height

點表示法只能接受字面量的成員的名字,不接受變量作為名字。

"this"的含義

greeting: function() {
  alert('Hi! I\'m ' + this.name.first + '.');
}

你也許想知道"this"是什么,關鍵字"this"指向了當前代碼運行時的對象( 原文:the current object the code is being written inside )——這里即指person對象。

Specialist classes

In this case we don't want generic people — we want teachers and students, which are both more specific types of people. In OOP, we can create new classes based on other classes — these new child classes can be made to inherit the data and code features of their parent class, so you can reuse functionality common to all the object types rather than having to duplicate it. Where functionality differs between classes, you can define specialized features directly on them as needed.

This is really useful — teachers and students share many common features such as name, gender, and age, so it is convenient to only have to define those features once. You can also define the same feature separately in different classes, as each definition of that feature will be in a different namespace. For example, a student's greeting might be of the form "Yo, I'm [firstName]" (e.g Yo, I'm Sam), whereas a teacher might use something more formal, such as "Hello, my name is [Prefix] [lastName]" (e.g Hello, My name is Mr Griffiths).

Note: The fancy word for the ability of multiple object types to implement the same functionality is polymorphism. Just in case you were wondering.

Constructors and object instances

Some people argue that JavaScript is not a true object-oriented language — for example it doesn't have a class statement for creating classes like many OO languages. JavaScript instead uses special functions called constructor functions to define objects and their features. They are useful because you'll often come across situations in which you don't know how many objects you will be creating; constructors provide the means to create as many objects as you need in an effective way, attaching data and functions to them as required.

When a new object instance is created from a constructor function, the functionality is not all copied over to the new object like "classic" OO languages — instead the functionality is linked to via a reference chain called a prototype chain (see Object prototypes). So this is not true instantiation, strictly speaking — JavaScript uses a different mechanism to share functionality between objects.

Let's explore creating classes via constructors and creating object instances from them in JavaScript. First of all, we'd like you to make a new local copy of the oojs.html file we saw in our first Objects article.

A simple example

1.Let's start by looking at how you could define a person with a normal function. Add this function below the existing code:

function createNewPerson(name) {
  var obj = {};
  obj.name = name;
  obj.greeting = function () {
    alert('Hi! I\'m ' + this.name + '.');
  }
  return obj;
}

2.You can now create a new person by calling this function — try the following lines in your browser's JavaScript console:

var salva = createNewPerson('salva');
salva.name;
salva.greeting();

This works well enough, but it is a bit longwinded; if we know we want to create an object, why do we need to explicitly create a new empty object and return it? Fortunately JavaScript provides us with a handy shortcut, in the form of constructor functions — let's make one now!
3.Replace your previous function with the following:

function Person(name) {
  this.name = name;
  this.greeting = function() {
    alert('Hi! I\'m ' + this.name + '.');
  };
}

The constructor function is JavaScript's version of a class. You'll notice that it has all the features you'd expect in a function, although it doesn't return anything or explicitly create an object — it basically just defines properties and methods. You'll see the this keyword being used here as well — it is basically saying that whenever one of the these object instances is created, the object's name property will be equal to the name value passed to the constructor call, and the greeting() method will use the name value passed to the constructor call too.

Note: A constructor function name usually starts with a capital letter — this convention is used to make constructor functions easier to recognize in code.

So how do we call a constructor to create some objects?
1.Add the following lines below your previous code addition:

var person1 = new Person('Bob');
var person2 = new Person('Sarah');

In each case, the new keyword is used to tell the browser we want to create a new object instance, followed by the function name with its required parameters contained in parentheses, and the result is stored in a variable.

2.Save your code and reload it in the browser, and try entering the following lines into your text input:

person1.name
person1.greeting()
person2.name
person2.greeting()

Other ways to create object instances

The Object() constructor

var person1 = new Object({
  name : 'Chris',
  age : 38,
  greeting : function() {
    alert('Hi! I\'m ' + this.name + '.');
  }
});

Using the create() method

JavaScript has a built-in method called create(), which allows you to create a new object instance based on an existing object.

var person2 = Object.create(person1);

20.原型

JavaScript 常被描述為一種基于原型的語言 (prototype-based language)——每個對象擁有一個原型對象,對象以其原型為模板、從原型繼承方法和屬性。原型對象也可能擁有原型,并從中繼承方法和屬性,一層一層、以此類推。這種關系常被稱為原型鏈 (prototype chain),它解釋了為何一個對象會擁有定義在其他對象中的屬性和方法。

準確地說,這些屬性和方法定義在 Object 的構造器函數之上,而非對象實例本身。

在傳統的 OOP 中,首先定義“類”,此后創建對象實例時,類中定義的所有屬性和方法都被復制到實例中。在 JavaScript 中并不如此復制——而是在對象實例和它的構造器之間建立一個連接(作為原型鏈中的一節),以后通過上溯原型鏈,在構造器中找到這些屬性和方法。

prototype 屬性:繼承成員被定義的地方

那么,那些繼承的屬性和方法在哪兒定義呢?如果你查看 Object 參考頁,會發現左側列出許多屬性和方法——大大超過我們在 person1 對象中看到的繼承成員的數量。某些屬性或方法被繼承了,而另一些沒有——為什么呢?

原因在于,繼承的屬性和方法是定義在 prototype 屬性之上的(你可以稱之為子命名空間 (sub namespace) )——那些以 Object.prototype. 開頭的屬性,而非僅僅以 Object. 開頭的屬性。prototype 屬性的值是一個對象,我們希望被原型鏈下游的對象繼承的屬性和方法,都被儲存在其中。

于是 Object.prototype.watch()、Object.prototype.valueOf() 等等成員,適用于任何繼承自 Object() 的對象類型,包括使用構造器創建的新的對象實例。Object.is()Object.keys(),以及其他不在 prototype 對象內的成員,不會被“對象實例”或“繼承自 Object() 的對象類型”所繼承。這些方法/屬性僅能被 Object() 構造器自身使用。

重要:prototype 屬性大概是 JavaScript 中最容易混淆的名稱之一。你可能會認為,這個屬性指向當前對象的原型對象,其實不是(還記得么?原型對象是一個內部對象,應當使用 proto 訪問)。prototype 屬性包含(指向)一個對象,你在這個對象中定義需要被繼承的成員。

create()

我們曾經講過如何用 Object.create() 方法創建新的對象實例。例如,在上個例子的 JavaScript 控制臺中輸入:

var person2 = Object.create(person1);

create() 實際做的是從指定原型對象創建一個新的對象。這里以 person1 為原型對象創建了 person2 對象。在控制臺輸入:

person2.__proto__

結果返回 person1 對象。

constructor 屬性

每個對象實例都具有 constructor 屬性,它指向創建該實例的構造器函數。

例如,在控制臺中嘗試下面的指令:

person1.constructor
person2.constructor

都將返回 Person() 構造器,因為該構造器包含這些實例的原始定義。

一個小技巧是,你可以在 constructor 屬性的末尾添加一對圓括號(括號中包含所需的參數),從而用這個構造器創建另一個對象實例。畢竟構造器是一個函數,故可以通過圓括號調用;只需在前面添加 new 關鍵字,便能將此函數作為構造器使用。

在控制臺中輸入:

var person3 = new person1.constructor('Karen', 'Stephenson', 26, 'female', ['playing drums', 'mountain clim]);

現在嘗試訪問新建對象的屬性,例如:

person3.name.first
person3.age
person3.bio()

正常工作。通常你不會去用這種方法創建新的實例;但如果你剛好因為某些原因沒有原始構造器的引用,那么這種方法就很有用了。

此外,constructor 屬性還有其他用途。比如,想要獲得某個對象實例的構造器的名字,可以這么用:

instanceName.constructor.name

具體地,像這樣:

person1.constructor.name

我們的代碼中定義了構造器,然后用這個構造器創建了一個對象實例,此后向構造器的 prototype 添加了一個新的方法:

function Person(first, last, age, gender, interests) {

  // 屬性與方法定義

};

var person1 = new Person('Tammi', 'Smith', 32, 'neutral', ['music', 'skiing', 'kickboxing']);

Person.prototype.farewell = function() {
  alert(this.name.first + ' has left the building. Bye for now!');
}

但是 farewell() 方法仍然可用于 person1 對象實例——舊有對象實例的可用功能被自動更新了。這證明了先前描述的原型鏈模型。這種繼承模型下,上游對象的方法不會復制到下游的對象實例中;下游對象本身雖然沒有定義這些方法,但瀏覽器會通過上溯原型鏈、從上游對象中找到它們。這種繼承模型提供了一個強大而可擴展的功能系統。

事實上,一種極其常見的對象定義模式是,在構造器(函數體)中定義屬性、在 prototype 屬性上定義方法。如此,構造器只包含屬性定義,而方法則分裝在不同的代碼塊,代碼更具可讀性。例如:

// 構造器及其屬性定義

function Test(a,b,c,d) {
  // 屬性定義
};

// 定義第一個方法

Test.prototype.x = function () { ... }

// 定義第二個方法

Test.prototype.y = function () { ... }

// 等等……

21.繼承

比如我們想要創建一個Teacher類,就像我們前面在面向對象概念解釋時用的那個一樣。這個類會繼承Person的所有成員,同時也包括:

一個新的屬性,subject——這個屬性包含了教師教授的學科。
一個被更新的greeting()方法,這個方法打招呼聽起來比一般的greeting()方法更正式一點——對于一個教授一些學生的老師來說。

定義 Teacher() 構造函數

我們要做的第一件事是創建一個Teacher()構造器——將下面的代碼加入到現有代碼之下:

function Teacher(first, last, age, gender, interests, subject) {
  Person.call(this, first, last, age, gender, interests);

  this.subject = subject;
}

這在很多方面看起來都和Person的構造器很像,但是這里有一些我們從沒見過的奇怪玩意——call()函數?;旧?,這個函數允許你調用一個在這個文件里別處定義的函數。第一個參數指明了在你運行這個函數時想對“this”指定的值,也就是說,你可以重新指定你調用的函數里所有“this”指向的對象。其他的變量指明了所有目標函數運行時接受的參數。

Note: In this case we specify the inherited properties when we create a new object instance, but note that you'll need to specify them as parameters in the constructor even if the instance doesn't require them to be specified as parameters (Maybe you've got a property that's set to a random value when the object is created, for example.)

As a note, we could have simply done this:

function Teacher(first, last, age, gender, interests, subject) {
  this.name = {
    first,
    last
  };
  this.age = age;
  this.gender = gender;
  this.interests = interests;
  this.subject = subject;
}

But this is just redefining the properties anew, not inheriting them from Person(), so it defeats the point of what we are trying to do. It also takes more lines of code.

Inheriting from a constructor with no parameters

Note that if the constructor you are inheriting from doesn't take its parameters from property values, you don't need to specify them as additional arguments in call(). So for example, if you had something really simple like this:

function Brick() {
  width: 10,
  height: 20
}

You could inherit the width and height properties by doing this (as well as the other steps described below, of course):

function BlueGlassBrick() {
  Brick.call(this);

  this.opacity: 0.5;
  this.color: 'blue';
}

Note that we've only specified this inside call() — no other parameters are required as we are not inheriting any parameters, only properties. Of course, you probably wouldn't do something this simple in a constructor very often as it defeats the point of using them. But it does get the point across.

Setting Teacher()'s prototype and constructor reference

All is good so far, but we have a problem. We have defined a new constructor, and it has a prototype property that by default just contains a reference to the constructor function itself (try entering Teacher.prototype.constructor into your JavaScript console at this point). We need to get Teacher() to inherit the methods defined on Person()'s prototype. So how do we do that?

Add the following line below your previous addition:

Teacher.prototype = Object.create(Person.prototype);

Giving Teacher() a new greeting() function

To finish off our code, we need to define a new greeting() function on the Teacher() constructor.

The easiest way to do this is to define it on Teacher()'s prototype — add the following at the bottom of your code:

Teacher.prototype.greeting = function() {
  var prefix;
if (this.gender === 'male' || this.gender === 'Male' || this.gender === 'm' || this.gender === 'M') {
    prefix = 'Mr.';
  } else if (this.gender === 'female' || this.gender === 'Female' || this.gender === 'f' || this.gender === 'F') {
    prefix = 'Mrs.';
  } else {
    prefix = 'Mx.';
  }

alert('Hello. My name is ' + prefix + ' ' + this.name.last + ', and I teach ' + this.subject + '.');
};

This alerts the teacher's greeting, which also uses an appropriate name prefix for their gender, worked out using a conditional statement.

Object member summary

To summarize, you've basically got three types of property/method to worry about:

1.Those defined inside a constructor function that are given to object instances. These are fairly easy to spot — in your own custom code, they are the members defined inside a constructor using the this.x = x type lines; in built in browser code, they are the members only available to object instances (usually created by calling a constructor using the new keyword, e.g. var myInstance = new myConstructor()).

2.Those defined directly on the constructor themselves, that are available only on the constructor. These are commonly only available on built-in browser objects, and are recognized by being chained directly onto a constructor, not an instance. For example, Object.keys().

3.Those defined on a constructor's prototype, which are inherited by all instances and inheriting object classes. These include any member defined on a Constructor's prototype property, e.g. myConstructor.prototype.x().

22.Working with JSON data

JSON can exist as an object, or a string — the former is used when you want to read data out of the JSON, and the latter is used when you want to send the JSON across the network. This is not a big issue — JavaScript provides a global JSON object that has methods available for converting between the two.

A JSON object can be stored in its own file, which is basically just a text file with an extension of .json, and a MIME type of application/json.

JSON requires double quotes to be used to be valid. It is safest to write it with double quotes, not single quotes.

Converting between objects and text

The above example was simple in terms of accessing the JSON, because we set the XHR to return the response already in JSON format, using:

request.responseType = 'json';

But sometimes we aren't so lucky — sometimes we'll receive some JSON data formatted as a text string, and we'll want to convert it to an object. And when we want to send JSON data as some kind of message, we'll often need to convert it to a string for it to work correctly. Luckily, these two problems are so common in web development that a built-in JSON object was added to browsers quite a while ago, containing the following two methods:

parse(): Accepts a JSON object in text string form as a parameter, and returns the corresponding object.

stringify(): Accepts a JSON object as a parameter, and returns the equivalent text string form.

You can see the first one in action in our heroes-finished-json-parse.html example (see the source code) — this does exactly the same thing as the example we built up earlier, except that we set the XHR to return the JSON as text, then used parse() to convert it to an actual JSON object. The key snippet of code is here:

request.open('GET', requestURL);
request.responseType = 'text'; // now we're getting a string!
request.send();

request.onload = function() {
  var superHeroesText = request.response; // get the string from the response
  var superHeroes = JSON.parse(superHeroesText); // convert it to an object
  populateHeader(superHeroes);
  showHeroes(superHeroes);
}

As you might guess, stringify() works the opposite way. Try entering the following lines into your browser's JavaScript console on by one to see it in action:

var myJSON = { "name": "Chris", "age": "38" };
myJSON
var myString = JSON.stringify(myJSON);
myString

Here we're creating a JSON object, then checking what it contains, then converting it to a string using stringify() — saving the return value in a new variable — then checking it again.

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,885評論 6 541
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,312評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 177,993評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,667評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,410評論 6 411
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,778評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,775評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,955評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,521評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,266評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,468評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,998評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,696評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,095評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,385評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,193評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,431評論 2 378

推薦閱讀更多精彩內容

  • **2014真題Directions:Read the following text. Choose the be...
    又是夜半驚坐起閱讀 9,761評論 0 23
  • (一) 覺順國三百三十四年,遭遇其他三國圍攻,情形嚴峻,而覺順國現任帝君又突然病重,宮中御醫皆手足無措。 覺順國遭...
    彼岸沙華閱讀 235評論 0 0