python基础知识

数据类型

整数:1,101,-8080,0,等等。
浮点数:3.14,1.234,1.23e9,2.3e8,等等。
字符串:以‘ ’或者“ ”括起来的任意文本。

# 字符串切片
s = 'ABCDEFG';
s[:3] # ==> 'ABC'
S[:-3] # ==> 'EFG'
S[::2] # ==> 'ACEG'

# 字符串可以通过 % 进行格式化,用指定的参数替代 %s。
d = { 'Adam': 95, 'Lisa': 85, 'Bart': 59 }
tds = ['<tr><td>%s</td><td>%s</td></tr>' % (name, score) for name, score in d.iteritems()]
# 字符串的join()方法可以把一个 list 拼接成一个字符串。
print '<table border="1"><tr><th>Name</th><th>Score</th><tr>'.join(tds).'</table>'
# <table border="1"><tr><th>Name</th><th>Score</th><tr><tr><td>Lisa</td><td>85</td></tr><tr><td>Adam</td><td>95</td></tr><tr><td>Bart</td><td>59</td></tr></table>

布尔值:True,False(注意大小写)。
空值:None。
list:list是一种有序的集合,可以随时添加和删除其中的元素。

# 创建list
L = ['Michael', 'Bob', 'Tracy']
# 空的list
empty_list = []
print L[0]    # ==> 'Michael'。使用索引时,千万注意不要越界。
print L[-1] # ==> 'Tracy'。倒序访问

# 向list中插入元素
L.append('Paul') # ['Michael', 'Bob', 'Tracy', 'Paul']
L.insert(0, 'Paul') # [ 'Paul', 'Michael', 'Bob', 'Tracy']
L.insert(1, 'Paul') # ['Michael', 'Paul', 'Bob', 'Tracy']

# 从list中删除元素
L.pop() # ==> 'Tracy',若不传参数pop()方法总是删掉list的最后一个元素,并且它还返回这个元素。
L.pop(1) # ==> 'Bob'

# 从list中替换元素
L[2] = 'Paul' # ['Michael', 'Bob', 'Paul']

# list 切片
L[0:3] # ['Michael', 'Bob', 'Tracy'],如果第一个索引是0,还可以省略。
L[1:3] # ['Bob', 'Tracy']
L[:] # ['Michael', 'Bob', 'Tracy'] 只用一个 : ,表示从头到尾。    L[:]实际上复制出了一个新listL[::2] # ['Michael', 'Tracy'] 第三个参数表示每N个取一个,上面的 L[::2] 会每两个元素取出一个来,也就是隔一个取一个。
L[-2:] # ['Bob', 'Tracy'] 倒序切片

# 用方法生成list
range(1, 11) # ==> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# 列表生成式
[x * x for x in range(1, 11)] # ==> [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
# 列表生成式中的条件过滤
[x * x for x in range(1, 11) if x % 2 == 0] # ==> [4, 16, 36, 64, 100]
# 多层表达式
[m + n for m in 'ABC' for n in '123'] # ==> ['A1', 'A2', 'A3', 'B1', 'B2', 'B3', 'C1', 'C2', 'C3']
# 翻译成循环代码就像下面这样:
L = []
for m in 'ABC':
    for n in '123':
        L.append(m + n)

tuple:tuple是另一种有序的列表,中文翻译为“ 元组 ”。tuple 和 list 非常类似,但是,tuple一旦创建完毕,就不能修改了。

# 创建tuple
t = ('Adam', 'Lisa', 'Bart')

# () 既可以表示tuple,又可以作为括号表示运算时的优先级,如下:
t = (1)
print t    # ==> 1
t = (1,)
print t # ==> (1,)

dict:花括号 {} 表示这是一个dict,然后按照 key: value, 写出来即可。最后一个 key: value 的逗号可以省略。

dict查找速度快,无论dict有10个元素还是10万个元素,查找速度都一样。而list的查找速度随着元素增加而逐渐下降。但是dict占用的内存比较大,还会浪费很多内容。list正好相反,占用内存小,但是查找速度慢。由于dict是按 key 查找,所以在一个dict中,key不能重复。

dict的的key-value序对是没有顺序的!这和list不一样。作为key 的元素必须不可变,Python的基本类型如字符串、整数、浮点数都是不可变的,都可以作为 key。但是list是可变的,就不能作为 key。

# 创建dict
d = {
    'Adam': 95,
    'Lisa': 85,
    'Bart': 59
}

# 计算集合的大小
len(d) # ==> 3

# 集合访问
print d['Adam'] # ==> 95。key不存在会报错用如下方法检测key是否存在
if 'Paul' in d:
    print d['Paul']
d.get('Bart') # ==> 59

# 更新dict
d['Paul'] = 72 # {'Lisa': 85, 'Paul': 72, 'Adam': 95, 'Bart': 59},如果 key 已经存在,则赋值会用新的 value 替换掉原来的 value

set:set 持有一系列元素,这一点和 list 很像,但是set的元素没有重复,而且是无序的,这点和 dict 的 key很像。set存储的元素和dict的key类似,必须是不变对象。

# 创建set,调用 set() 并传入一个 list
s = set(['A', 'B', 'C'])
print s    # ==> set(['A', 'C', 'B'])

# set不包含重复元素
s = set(['A', 'B', 'C', 'C'])
print s    # ==> set(['A', 'C', 'B'])

# set访问
#由于set存储的是无序集合,所以我们没法通过索引来访问,访问 set中的某个元素实际上就是判断一个元素是否在set中。可以用 in 操作符判断
'B' in s # ==> True

# 更新set
s = set([1, 2, 3])
s.add(4) # set([1, 2, 3, 4])
s.remove(4) # set([1, 2, 3])
s.add(3) # set([1, 2, 3])

语句

print语句可以向屏幕上输出指定的内容。

# print语句也可以跟上多个字符串,用逗号“,”隔开,就可以连成一串输出
print 'The quick brown fox', 'jumps over', 'the lazy dog'
print 300+200+100

print "\"To be, or not to be\": that is the question.Whether it\'s nobler in the mind to suffer."
# 我们可以在字符串前面加个前缀 r ,表示这是一个 raw 字符串,里面的字符就不需要转义了。
print r'''"To be, or not to be": that is the question.Whether it's nobler in the mind to suffer.'''

for循环(迭代)

# 遍历(迭代)list
L = ['Adam', 'Lisa', 'Bart']
for name in L:
    print name # Adam Lisa Bart

# 遍历(迭代)dict
d = { 'Adam': 95, 'Lisa': 85, 'Bart': 59 }
for key in d:
    print key,':',d[key] # ==> Adam: 95 Lisa: 85 Bart: 59
# 迭代 dict 的value。
# values() 方法,这个方法把dict转换成一个包含所有valuelistfor v in d.values():
    print v # 85 95 59
# itervalues() 方法不会转换,它会在迭代过程中依次从 dict 中取出 value,所以 itervalues() 方法比 values() 方法节省了生成 list 所需的内存。但是迭代的效果是一样的
for v in d.itervalues():
    print v # 85 95 59
# 迭代 dict 的key和valuefor key, value in d.items():
    print key, ':', value
# dict 对象的 items() 方法返回的值:
print d.items() # ==> [('Lisa', 85), ('Adam', 95), ('Bart', 59)]

# 遍历(迭代)set
s = set(['Adam', 'Lisa', 'Bart'])
for name in s:
    print name # ==> Lisa Adam Bart

# Python中,迭代永远是取出元素本身,而非元素的索引,想在for循环中拿到索引,可以使用enumerate() 函数。
L = ['Adam', 'Lisa', 'Bart', 'Paul']
for index, name in enumerate(L):
    print index, '-', name
# numerate() 函数把上述的list变成了类似[(0, 'Adam'), (1, 'Lisa'), (2, 'Bart'), (3, 'Paul')]的一个list,因此,迭代的每一个元素实际上是一个tuple。
for t in enumerate(L):
    index = t[0]
    name = t[1]
    print index, '-', name
# 如果我们知道每个tuple元素都包含两个元素,for循环又可以进一步简写为上述的形式。

四则运算

Python支持对整数和浮点数直接进行四则混合运算,运算规则和数学上的四则运算规则完全一致。

1 + 2 + 3   # ==> 6
4 * 5 - 6   # ==> 14
7.5 / 8 + 2.1   # ==> 3.0375

# 小括号提升优先级
(1 + 2) * 3    # ==> 9
(2.2 + 3.3) / (1.5 * (9 - 0.3))    # ==> 0.42145593869731807

# Python的整数运算结果仍然是整数,浮点数运算结果仍然是浮点数
1 + 2    # ==> 整数 3
1.0 + 2.0    # ==> 浮点数 3.0

# 但是整数和浮点数混合运算的结果就变成浮点数了
1 + 2.0    # ==> 浮点数 3.0
11.0 / 4    # ==> 2.75

# 求余的运算
11 % 4    # ==> 3

逻辑运算

# 与运算:只有两个布尔值都为 True 时,计算结果才为 TrueTrue and True   # ==> True
True and False   # ==> False
False and True   # ==> False
False and False   # ==> False

# 或运算:只要有一个布尔值为 True,计算结果就是 TrueTrue or True   # ==> True
True or False   # ==> True
False or True   # ==> True
False or False   # ==> False

# 非运算:把True变为False,或者把False变为Truenot True   # ==> False
not False   # ==> True

函数

Python内置函数的调用:

# 要调用一个函数,需要知道函数的名称和参数,如果传入的参数数量不对,会报TypeError的错误。
abs(-20) # ==> 20
cmp(1, 2) # ==> -1
cmp(3, 2) # ==> 1

定义函数和调用:在Python中,定义一个函数要使用 def 语句,依次写出函数名、括号、括号中的参数和冒号:

def my_abs(x):
    if x >= 0:
        return x
    else:
        return -x

# 定义默认参数
def greet(name='world'):
print 'Hello, ' + name + '.'
greet()    # ==> Hello, world
greet('Bart') # ==> Hello, Bart

# 定义可变参数
def fn(*args):
    print args
fn() # ==> ()
fn('a') # ==> ('a',)
fn('a','b') # ==> ('a','b')

ECMAScript5中新增的数组方法总结

every():测试断言函数是否对每个数组元素为真

array.ervery(predicate,o);

参数predicate:断言函数,有三个参数predicate(array[i],i,array),返回值被当做布尔值解析。所有数组元素通过断言函数的返回值都可以被转换成布尔真时,every()方法返回true;

参数o:调用predicate时可选的this值,可选;

例子:

[1,2,3].every(function(x){return x<5}); //=>true:所有元素都小于5
[1,2,3].every(function(x){return x<3}); //=>false:不是所有元素都小于3
[].every(function(x){return false}); //=>true:[]总是返回true

filter():过滤数组元素,返回一个满足条件的新数组

array.filter(predicate,o);

参数predicate:过滤函数,有三个参数predicate(array[i],i,array),按序号从小到大遍历array,每个数组元素调用一次predicate,若返回值为真,则该数组元素会添加到返回的数组中;

参数o:调用predicate时可选的this值,可选;

例子:

[1,2,3].filter(function(x){return x>1}); //=>[2,3]

forEach():便利数组元素,并对每一个元素调用一次f

array.forEach(f,o);

参数f:为array的每一个元素调用的函数(序号从小到大);

参数o:调用f时可选的this值,可选;

例子:

var a = [1,2,3];
a.forEach(function(x,i,array){array[i]++}); //a现在的值是[2,3,4]。forEach没有返回值。

indexOf():在数组中查找值

array.indexOf(value,start);

参数value:要查找的值;

参数start:开始查找数组元素的序号,可选参数,不传则从0开始查找;

若找到该值(用'===='判断),则方法返回该值的索引位置,若没有找到,返回-1。

例子:

['a','b','c'].indexOf('b');  // => 1
['a','b','c'].indexOf('a',1);  // => -1
['a','b','c'].indexOf('d');  // => -1

lastIndexOf():在数组中查找值(反向)

array.lastIndexOf(value,start);

参数value:要查找的值;

参数start:开始查找数组元素的序号,可选参数,不传则从最后一个元素开始查找;

若找到该值(用'===='判断),则方法返回该值的索引位置,若没有找到,返回-1,查找的方向与indexOf相反。

map():按某种规则处理每个数组元素,返回一个等长的新的数组

array.map(f,o);

参数f:数组的处理函数,有三个参数f (array[i],i,array),该方法返回的值会成为map方法返回数组的一个元素,索引为i

参数o:调用f时可选的this值,可选;

例子:

[1,2,3].map(function(x){return x*x;});  // => [1,4,9]

reduce():从数组中计算出一个值

详见javascript reduce方法

reduceRight():从数组中计算出一个值(从右到左)

reduceRight()方法与reduce()方法只有一点不同:reduceRight()方法遍历数组的时候是从右到左(从最大序号到最小序号)。

some():测试是否有数组元素满足断言函数

参数predicate:断言函数,有三个参数predicate(array[i],i,array);

参数o:调用predicate时可选的this值,可选;

some()方法与every()很相似,不同的是,some()并不需要所有的数组元素在调用predicate时都返回真,只需要有一个元素在调用predicate时返回真,some()就会停止遍历,并返回true。

DOM

DOM概览

DOM(文档对象模型)是表示和操作HTML文档内容的基础API。用DOM树对象表示HTML文档的嵌套元素,包括HTML标签、表示文本字符串的节点,也可能包含HTML的注释节点。

选取文档元素

树形的根部是document节点,代表整个文档,代表HTML元素的是Element节点,代表文本的是text节点。这些节点都是Node的子类。JavaScript避免不了要操作DOM,这时可以使用全局变量document来引用DOM对象,同时,DOM也定义了许多方式来选取元素,如通过指定的id属性,name属性,标签名字,指定的CSS类等。

1、通过id查找多个元素:

/*函数接受任意多个字符串参数,每个参数将作为元素id传入*/
function(/ids.../){
    var elements = {};
    for(var i=0;i<arguments.length;i++){
        var id = arguments[i];
        var elt = document.getElementById(id);
        if(elt == null){
            thow new Error("No element with id:"+id);
        }else{
            elements[id] = elt;
        }
    }
    return elements;
}
//低于IE8版本的浏览器中,getElementById()方法对匹配元素的ID不区分大小写,同时也会匹配到那么属性符合条件的元素。所以,注意设置name和id属性的时候尽量让这两个元素不要重名。

2、用过name属性和标签名来查找元素,分别使用getElementByName()和getElementsByTagName()方法,这两个方法都是返回一个NodeList对象。像document.images或者document.forms的属性为HTMLCollection对象
这两种都是只读的类数组对象,他们有length属性,也可以像数组一样索引(只能读),但是不能在这两类对象上直接调用数组的方法,但是可以间接的调用:

//获取p标签中的内容
var content = Array.prototype.map.call(document.getElementByTagName("p"),function(e){ return e.innerHTML;});

值得注意的是NodeList对象HTMLCollection对象都是实时的。
同时,Element类也定义了getElementsByTagName()方法,该方法只在调用该方法的元素对象的后代中选取元素。

3、通过HTML的class属性选择元素,使用getElementsByClassName()方法,返回的是一个实时的NodeList对象,该方法需要一个字符串参数,可以是由多个空格隔开的标识符组成。

//查找包含fatal和error类的元素,使用
var ele = getElementsByClassName('fatal error');

4、使用querySelector方法返回文档中匹配指定 CSS 选择器的第一个元素。

document.querySelector(CSS selectors);
document.querySelector("p");//返回第一个p标签元素
document.querySelector(".container");//返回class等于container的第一个元素
document.querySelector(".container header");//返回class等于container中的第一个header子元素

5、使用querySelectorAll方法返回所有文档中匹配指定 CSS 选择器的元素,与querySelector相同,只不过querySelectorAll方法返回一个匹配的nodeList。

文档结构

一旦从文档中选取了一个元素,有时需要查找文档中与之在结构上相关的部分,这种结构可以看作一个节点对象树。

1、节点树的文档。我们之前说的Document对象,和他的Element对象,文档中表示文本的Text对象都是属于Node对象,对于一个节点来讲,他具有以下的属性,

parentNode:父节点(document没有父节点,值为null),任何元素的父节点都不可能是Text。任何Elemnt的parentNode总是一个Element。
childNodes:子节点的实时表示
firstChild、lastChild:子节点的第一个和最后一个。不存在则为nul
nextSibling、previousSibling:兄弟节点中的前一个和下一个。
nodeType:节点的类型(9代表Document节点,1代表Element节点,3代表Text节点,11代表DocumenFragment节点)
nodeValue:Text节点的文本内容
nodeName:元素的标签名(大写)

例如有这样的一段HTML:<html><head></head><body>hello word!</body></html>
下面的方法可以获取到body

document.childNodes[0].childNodes[1];//选择到了body元素节点的引用
document.firstChild.firstChild.nextSibling;

API对文档的变化极其敏感,若我们在headhtml之间插入一个新行,那么表示这个新行的Text节点就是文档下面的第一个子节点了。

2、元素树的文档。这个应该是比较熟悉的内容,当我们想忽略部分文档,如Text节点的时候,可以使用另一个API(基于元素文档遍历API):

第一部分,children属性

该属性类似childNodes,也是一个NodeList对象,但是它只包含Element。并且Text节点和Commont节点没有children属性。

第二部分,Element属性

//类似于firstChild和lastChild,不同的是只代表子Element
firstElementChild,lastElementChild
//类似于nextSibling和lastSibling,不同的是只代表兄弟Element
nextElementSibling,previousElementSibling
//子元素的个数,和children.length相等
childElementCount

元素的属性

元素的属性应该都是比较熟悉的了,如id,class,src,以及一些时间的处理如onclick等,值得注意的是,有些属性在JavaScript中是保留字,如class,在JavaScript中把他变为ClassName。

getAttribute()//获取html属性,返回的是一个字符串,
setAttribute()//设置元素的属性
hasAttribute()//判断元素属性是否存在,
removeAttribute()//完全删除属性

var width = parseInt(images.getAttribute("WIDTH));
images.setAttribute('class','thumbnail");

还有一种使用Element属性的方法,Node类型定义了attribute属性。

document.body.attributes[0];//返回body元素的第一个属性
document.body.attributes.bgcolor;//返回body元素的第一个属性

html5文档中中表明,任意以data-为前缀的小写的属性名字都是合法的。这些数据集属性会以属性的形式存在于该元素的dataset属性内。如data-x的值保存在dataset.x内,带连字符的属性采用驼峰命名方式。

元素的内容

Element的innerHTML属性会返回元素的内容,内容可以是HTML字符串,也可以是纯文本的字符串。
查询和插入纯文本形式的内容,可以使用Node对象的textContent属性实现,在ie中使用innerHTML替代。

/**
*一个参数,返回element的textContent或者innerText
*两个参数,设置elemnet的textContent或者innerText
*/
function(element,value){
    var content = element.textContent;
    if(value === undefined){
        //没有传递value
        if(content !== undefined) return content;
        else return element.innerText;
    }
    else{
        if(content !== undefined) element.textContent = value;
        else element.innerText = value;
    }
}

创建、插入和删除节点

创建新的元素节点可以使用Document对象的createElement()方法。给方法传递元素的标签名(不区分大小写)。创建Text节点可以使用document.createTextNode()方法。

var newNode = document.createTextNode("this is a textNode");
var s = document.createElement("script")

也可以选择clone已经存在的节点作为创建节点的另一种方式,每个节点都有一个cloneNode()方法,该方法返回该节点的一个全新副本, 给方法传递参数true可以递归的复制所有的后代节点。

var cloneNode = document.getElementById("test").cloneNode(true);

将创建的节点插入到文档中的方法有appendChild()insertBefore()
appendChild()在需要插入的元素上调用,并将节点作为他最后一个子节点插入。

var ele = document.getElementById("test");
var span = document.createElement("span");
ele.appendChild(span);

insertBefore()appendChild()类似,也是在被插入的元素节点上调用.不同的是insertBefore()接受两个参数,第一个是待插入的元素,另一个是在被插入元素中已经存在的节点(该方法将新节点插入到该节点之前)。

//当位置索引大于被插入元素的子元素个数时,元素会被插入到末尾
ele.insertBefore(span,ele.children[0]);

当用这两个方法将已经存在于文档中的节点再插入一次的时候,那个节点将自动从他当前的位置删除,并在新的位置插入,不需要显示删除节点。


removeChild()从文档中删除节点,同样也是在被删除节点的父节点上调用,被删除节点作为参数传入方法。

n.parentNode.removeChild(n)

replaceChild()方法删除一个子节点,并用一个新的子节点取而代之。在父节点上调用该方法,第一个参数是新节点,第二个参数是需要替代的节点。

n.parrentNode.replaceChild(n.parrentNode.replaceChild(document.createTextNode("this is a text node"),n);

replaceChild()另一种用法:
//用元素包裹节点n
function embolden(n){
//假如参数为字符串而不是节点,将其当作元素的id
if(typeof n == ‘string’) n = document.getElementById(n);
var parrent = n.parrentNode; //获得n的父节点
var b = document.createElement(“b”);
parrent.replaceChild(b,n);
b.apperndChild(n);
}

文档片段

DocumentFragment是一种特殊的Node,他做为一个节点的临时容器存在。使用如下方法创建一个文档片段:

var frag = document.createDocumentFragment()

文档碎片是独立的,没有父节点,但是可以有任意多的子节点,所以插入元素的方法在文档碎片上同样适用。文档片段可以将一组节点被当做一个节点来看待,当我们把一个文档片段插入到文档中的时候,实际上是将文档片段中的节点插入到了文档中,二不是文档片段本身。

《javascript面向对象精要》总结(三)

构造函数和原型对象

构造函数:就是使用new创建对象时调用的函数,然而构造函数也是函数,可以用定义函数的方法定义它,唯一的区别是构造函数的函数名首字母应该大写,用此来与其他函数进行区别。

function Person(){
    //空的构造函数
}
var person1 = new Person;    //若没有需要传递给构造函数的参数,括号可以省略。

//person1是Person的一个实例,可以使用instanceof操作符获取对象的类型,每个对象在创建的时候都自动拥有一个构造函数属性,其中包含了一个指向其构造函数的引用。

console.log(person1 instanceof Person);        //true

//通过对象字面形式或Object构造函数创建出来的泛用对象,其构造函数属性只想Object,那些通过自定义构造函数创建出来的对象,其构造函数指向创建它的构造函数。

console.log(person1.constructor=== Person);        //true

很明显,空的构造函数没有什么卵用,使用构造函数的目的是为了创建许多拥有相同属性和方法的对象,你需要做的事在构造函数内给this添加属性:

function Person(name){
    this.name = name;
    this.sayName = function(){
        console.log(this.name);
    };
}
var person = new Person("Shi");
console.log(person.name);    // "Shi"
person.sayName();    // "Shi"

构造函数本身不需要返回值,new会帮你返回,你也可以在构造函数中显示调用return。如果返回的值是一个对象,它会替代新创建的对象实例返回,如果返回值是一个原始类型,它会被忽略。尽量使用new调用构造函数,不然有可能改变全局变量。

但是如果我们初始化好多个Person类的实例对象,那么每一个对象都有一个name属性和一个sayName()方法,存在代码冗余,而原型就帮我们实现了所有对象实例共享一个方法,是同this来访问正确的数据。

function Person(name){
    this.name = name;
}
Person.prototype.sayName = function(){
    console.log(this.name);
}
//原型是javascript实现继承的基础,之前的文章有过记录,这里就不再多解释了。
//但是当使用对象的字面形式改写原型对象改变了构造函数的属性,使他指向Object而不是你创建的类。这是因为原型对象具有一个constructor属性,这是其他对象没有的。当一个函数被创建时,它的prototype属性也被创建,而该原型对象的constructor属性指向该函数。所以我们需要这样去写:

Person.prototype = {
    constructor:Person,
    sayName : function(){
        console.log(this.name);
    }
};

可以发现对象和构造函数之间没有直接的联系,不过对象实例和原型对象以及原型对象和构造函数之间有直接的联系。

对象模式

javascript对象的所有属性都是公有的,但是有时你可能不希望数据公有。

模块模式:看看下面的代码

var yourObject = (function(){

    //private data

    return {
        //public method and properties
    };
}());
//这种语法会立即执行这个匿名函数,也就是函数仅存在于被调用的瞬间,执行之后就被销毁了。

模块模式实现了使用普通变量作为私有变量,并且通过创建闭包函数作为对象方法来操作他们:

var person = (function(){
    var age = 25;
    return {
        name : "Shi",
        getAge : function(){
            return age;
        },
        growOlder : function(){
            age++;
        }
    };
}());

console.log(person.name);
console.log(person.getAge());
person.age = 100;
console.log(person.getAge());
person.growOlder();
console.log(person.getAge());

构造函数的私有成员:

var Person = (function(){
    //所有对象的实例都共享这个age
    var age = 25;
    function InnerPerson(name){
        this.name = name;
    }
    InnerPerson.prototype.getAge = function(){
        return age;
    }
    InnerPerson.prototype.growOlder = function(){
        age++;
    }
    return InnerPerson;
}());
var person = new Person('Shi');
console.log(person.name);
console.log(person.getAge());
person.age = 100;
console.log(person.getAge());
person.growOlder();
console.log(person.getAge());

《javascript面向对象精要》总结(二)

理解对象

属性的定义,探测,删除:

我们知道,我们可以在任何时候给js的对象去添加属性。至于添加属性的具体过程,是javascript在对象上调用了一个叫[[Put]]的内部方法,该方法会在对象上创建一个节点来保存属性(自有属性)。当已有的属性被设置一个新的值时,调用的是一个叫[[Set]]的方法,该方法将属性的当前值替换成新值。判断一个属性是否存在,有时我们会这样做:

if(person.name){
    //do something
}

但是,当name的值是一个对象,非空字符串,非零数字或者true时,会评估为真,但是当值是null,undefined,0,false,NaN或空字符串时,会被评估为假。显而易见这里就会存在一些问题了。

更加可靠的是使用in操作符,用法都知道,不说了。但是in操作符还会检查原型属性,这有可能也不是我们想要的,我们还可以使用hasOwnProperty()这个方法。每个javascript对象都有这个方法。

当我们想要删除一个对象的某个属性的时候,使用delete操作符就可以了。

属性的枚举:

典型的方法是使用for-in循环进行迭代(只能遍历可枚举的属性),他也会遍历原型属性:

var property;
for(property in object){
    console.log("Name:"+property);
    console.log("Value:" + object[property]);
}

ECMAScript5中引入了Object.keys()方法,可以获取对象的属性名字数组(自有属性,不包含原型属性):

var property = Object.keys(object);
var i len;
for (i=0,len=peoperty.length;i<len;i++){
    console.log("Name:"+property[i]);
    console.log("Value:" + object[property[i]]);
}
属性的类型:

属性的类型有两种,一种是数据属性,我们之前用到的属性都是数据属性,数据属性包含一个值。
访问器属性不包含值,是定义了一个当前属性被读取时调用的函数,和一个当属性被写入是调用的函数(可以是一个也可以是两个都有)。

var person = {
    _name:"Shi",//前置下划线是一个俗称的命名规范,表示该属性被认为是私有,但是实际上还是公开的。

    get name(){
        return this._name;
    },

    set name(value){
        this._name = value;
    }
};

console.log(person.name);
person.name = "SHI";
console.log(person.name);
属性特征:

通用的属性特征:[[Enumerable]]决定了属性是否可以被枚举,[[Configurable]]决定了属性是否可以被配置。我们平时声明的属性都是可枚举,可配置的,所以你可以随时删除和更改。改变属性的特征可以使用Object.defineProperty()方法,该方法接受三个参数:拥有该属性的对象,属性名,包含需要设置的特征的属性描述对象:

var person = {
    name:"Shi"
}

Object.defineProperty(person,"name",{
    enumerable:false
});

console.log("name" in person);    //true
console.log(person.propertyIsEnumerable("name"));    //false

var properties = Object.keys(person);
console.log(properties.length);        // 0

Object.defineProperty(person,"name",{
    configurable:false
});

delete person.name;        //严格模式下会导致错误,非严格模式会失败。
console.log(person.name);    //"Shi"

//状态已经变成不可配置,不能修改了。
Object.defineProperty(person,"name",{
    configurable:true
});

数据的属性特征:[[Value]]包含属性值,当你在对象上创建属性时该特征被自动赋值。[[Writable]]是一个布尔值,指示该属性是否可以写入。所有属性都是可写的,除非你另外指定。

var person = {};

Object.defineProperty(person,"name",{
    configurable:true,
    enumerable:true,
    value:"Shi",
    writable:true
});
//Object.defineProperty()定义新的属性时,要为所有的特征值制定一个值,否则布尔型的特征值会被制定为false。

访问器属性特征:访问其属性不需要存值,所以没有[[Value]]和[[Writable]],取而代之的是[[Get]]和[[Set]]。

var person = {
    _name:"Shi"
};
Object.defineProperty(person,"name",{
    get:function(){
        return this._name;
    },
    set:function(value){
        this._name = value;
    },
    enumerable:true,
    configurable:true
});

当然,从上面的代码可以知道,当我们想要创建一个只能读取的属性时,只需要定义一个get访问器属性就可以了。其他的特征属性不设置则默认为(false)。下面说说获取属性特征的方法:

var person = {
    name:"Shi"
};
var descriptor = Object.getOwnPropertyDescriptor(person,"name");
console.log(descriptor.enumerable);
console.log(descriptor.configurable);
console.log(descriptor.writable);
console.log(descriptor.value);
//非常清晰,不多说。
禁止修改对象:

对象也和属性一样,具有指导其行为的内部特征。[[Extensible]]指明该对象本身是否可以被修改。我们之前创建的所有对象默认都是可以扩展的,这意味着新的属性可以随时被添加。当我们设置了[[Extensible]]为false,就可以禁止新的属性被添加。有三种方法可以锁定对象属性。
禁止扩展:使用Object.preventExtensions()创建一个不可扩展的对象。该方法接受一个参数,就是你希望使其不可扩展的对象。可以使用Object.isExtensible()来检查[[Extensible]]的值。

var person = {
    name:"Shi"
};
console.log(Object.isExtensible(person));    //true
Object.preventExtensions(person);
console.log(Object.isExtensible(person));    //false

person.sayName = function(){
    console.log(this.name);
}
console.log("sayName" in person);    //false

对象封印:被封印的对象不可扩展而且所有的属性都不可配置,也就是不仅不能添加属性,也不能删除属性或者改变其类型,只能读取属性的值。可以是用Object.seal()来封印一个对象,该方法调用的时候,该属性的[[Extensible]]设置为false,其所有属性的[[configurable]]特征被设置为flase。可以使用Object.isSealed()来判断一个对象是否被封印。

var person = {
    name:"Shi"
};
console.log(Object.isExtensible(person));    //true
console.log(Object.isSealed(person));    //false

Object.seal(person);
console.log(Object.isExtensible(person));    //false
console.log(Object.isSealed(person));    //true

person.sayName = function(){
    console.log(this.name);
}
console.log("sayName" in person);    //false
person.name = "Grey";
console.log(person.name);    //"Grey"

对象冻结:和封印一样,但是冻结的对象是不能写入数据的。使用Object.freeze()来冻结一个对象。使用Object.isFrozen()来判断一个对象是否被冻结。

Object.freeze(person);
person.name = "Grey";
console.log(person.name);    //"Shi"

js对象(1)

对象是JavaScript的基本数据类型,是复合值,在JavaScript中除了字符串、数字、true、false、null和undefined之外,JavaScript中的值都是对象。

创建JavaScript对象

使用对象直接量,也就是名值对组成的映射表,格式如下:

var empty = {};        //这是一个空对象
var point = { x:0 , y:1 };         //这个对象有连个属性
var car = {
    name:'BMW X7',
    color:'black',
    'max-speed':'200'    //属性名中间有空格或-,必须用字符串表示
}

使用new创建对象:

var myArray = new Array();        //创建一个数组,等同于[]
var date = new Date()         //创建一个Date对象

原型

绝大多数JavaScript对象都会和另一个对象关联,这种关联就涉及到了原型,当通过关键字new和构造函数创建对象的时候,实际上是构造该对象的prototype属性,JavaScript对象具有自有属性,也有一些属性是从原型对象继承而来的,所以通过new Array()创建的对象的原型就是Array.prototype。

举个例子,当我们要查询对象ooo中的x属性,如果o中不存在x,那么会在o的原型中继续查找x。如果原型对象中也没有x,那么会在这个原型对象的原型上继续查询,知道找到这个x,或者直到某个对象的原型是null为止,这样我们可以清楚的发现,对象的原型属性构成了一个链,通过这个链,就可以实现属性的继承(图片来源)。

图片

ECMAScript5中定义了一个Object.creat()的方法,创建一个新的对象,其中的一个参数就是对象的原型,同时还有第二个参数,是对对象的属性的进一步描述。

var o = Object.creat({x:1,y:1});    //o继承了属性x和y

如果我们传入了null参数,这时创建的对象就不会继承任何东西(基础方法也不会继承),比如toString(),如果想要创建一个新对象需要传入Object.prototype。下面这个方法可以模拟原型继承:

function inherit(p){
    if(p==null) throw TypeError();
    if(Object.creat)
        return Object.creat(p);
    var t = typeof p;
    if(t !== "object" && t !== "function") throw TypeError();
    function f() {};
    f.prototype = p;
    return new f();
}

对象属性的查询和设置

对象的属性可以通过’.‘或者’[]‘进行查询,简单的说,当使用’.‘的时候,’.‘的右边一定要是对象的属性名,当使用’[]‘的时候,’[]‘内必须是一个计算结果为字符串的表达式,也可以直接是字符串。同时我们也可以使用’.‘和’[]‘来设置属性值。

var book={
    name:'AngularJs'
    title:'Easy Study'
    }
console.log(book.name);            //AngularJs
console.log(book[name]);        //AngularJs
book.color = 'red';
book.pages = 1000;
console.log(book.color);        //red
console.log(book[pages]);        //1000

值得注意的是,在我们的程序中可能经常会出现这样的代码,

var length = book.subtitle.length;        //这会抛出一个错误,因为book.subtitle本身是undefined的,并没有length属性,除非你确定subtitle和book都是对象,否则尽量不要这样去写。可以用下面的两种方式。
//方法一
var length = undefined;
if(book){
    if(book.subtitle) len = book.subtitle.length;
}
//方法二:短路写法
var length = book && book.subtitle && book.subtitle.length;

对象属性的删除

删除属性可以用delete运算符,但是delete只能断开属性和宿主对象的关系,而不会去操作属性中的属性,说白了,就是当一个对象的属性的值的类型还是一个对象的时候,当我们删除了这个属性,这个作为属性的这个对象依然可以使用的。

a = {p:{x:1}};
b = a.p;
delete a.p;
console.log(b.x);
//输出1,因为已经删除的属性的引用依然存在。这种问题有可能导致内存泄露,所以在删除对象属性的时候,一定要删除的彻底。遍历所有的属性,并且都删除。

js操作字符串、数组的常用方法总结

对于像我这种记忆力不是很好的人,这样的整理还是很有必要的,虽然是很常用的方法,有时候也会因为强迫症要验证一次,在这里总结一下,方便以后查看。

字符串

在Javascript中字符串是固定不变的,也就是说,当使用某种方法处理字符串的时候,类似于replace()等,会返回一个新的字符串,也就是说字符串是不能被修改的。

可以使用“+”号将两个字符串进行连接:

msg = "hello,"+"word";  //生成字符串hello,word

字符串具有length属性,可以使用charAt()方法配合length属性返回字符串某个位置上的元素:

var s = "hello,word";
s.charAt(0)        //返回 h
s.charAt(s.length-1)    //        返回 d

当需要根据索引截取一个字符串的时候,可以使用substring()方法或者slice()方法:

s.substring(1,4)        //返回索引位置为1、2、3的字符组成的字符串‘ell’;
s.slice(1,4)            //同上返回‘ell’
s.slice(0)                //返回整个字符串‘hello word’
s.slice(-3)                //从末尾开始返回后三个字符‘ord’
s.clice(0,-3)            //返回‘ellow,w’
//负值的索引,可以理解为从最后的位置开始算起,但是并不存在‘-0’

当需要查找摸个字符在字符串中的位置的时候,可以使用indexOf()方法,返回该字符的索引位置:

s.indexOf("o");            //返回“4”,字符“o”首次出现位置的索引;
s.lastIndexOf("o")        //这是最后一次出现“o”的位置,返回7;
s.indexOf("1",3);        //这是返回在位置3以及3以后首次出现“o”的索引,返回4;

可以使用split()方法将字符串分割成数组:

s.split(",")            //根据“,”这一标识符,将字符串分割成一个数组,返回的数组为['hello','word'],值得注意的是这里面已经不包含“,”这个字符了。

同样,我们还可以使用replace()方法替换字符串中的某个字符:

s.replace("h","H");        //返回“Hello,word”,但是如果直接用“h”,作为标示,这个方法只会替换一次,也就是说如果字符串中有许多的“h”,也只会替换一次。想要全部替换可以选择使用正则表达式;
s.replace(/\l/g,"L");    //这样就可以换掉所有的“l”;
s.toUpperCase();        //返回“HELLO,WORD”;

数组

可以使用push()方法,向一个数组的末尾添加元素:

var arr = ['heool','word'];
arr.push('!');        //arr=['heool','word','!'];

对于数组的遍历,有两种方法,一种是根据数组的length属性,利用for循环进行遍历;另一种可以使用for/in循环,

for(var index in array){
    var value  = array[index];
}

数组也可以转换成字符串,使用join()方法,可以将数组转换成字符串:

var arr = ['1','2','3'];
arr.join(",");        //返回“1,2,3”;

sort()方法可以对数组进行排序,当不传入参数时,数组元素以字母表顺序排序:

var arr = ['banana','cherry','apple'];
arr.sort();
var str = a.join(",");    //str的值是“apple,banana,cherry”;
//如果数组中包含undefined,sort方法会把它放在数组的尾部。
//sort方法把数组中的每个对象转换成字符串,然后进行比较,那么 11 会被放在 2 的前面。所以我们需要传递一个函数给sort用来比较纯数字的大小,这个函数有两个参数,排序的规则是根据这个函数的返回值,返回值大于零,第一个参数在后,返回值小于零,第一个参数在前,返回值等于零,顺序无所谓。
a.sort(function(a,b){return a-b;});

连接两个数组可以使用concat(),这个方法返回一个连接好的新数组:

var a = [1,2,3];
a.concat(4,5);        //返回[1,2,3,4,5];
a.concat([4,5]);    //返回[1,2,3,4,5];

数组也有slice()方法,slice()方法不会修改原来的数组,而是返回一个新的数组:

var a = [1,2,3,4,5];
a.slice(0,3);    //返回[1,2,3];
a.slice(3);        //返回[4,5];
a.slice(1,-1);    //返回[2,3,4];参数-1指定了最后一个元素
a.slice(-3,-2);    //返回[3];

slice()能够从数组中删除元素、插入元素到数组中或者同时完成这两件事情,var a = [1,2,3,4,5,6,7,8];a.splice(4),当只有一个参数的时候,从数组的起点位置开始到数组结尾的所有元素都会被删除,这个方法会返回被删除的[5,6,7,8],这时数组a的值是[1,2,3,4],a.splice(1,2);返回[2,3],现在a的值是[1,4],a.splice(1,0,’a’,’b’); //返回[‘a’,’b’],现在a的值是[1,’a’,’b’,4];

向数组中添加或者删除元素,还有push(),pop(),unshift(),shift()四个方法,分别是想数组末尾添加元素,从数组末尾删除元素,在数组头添加一个或多个元素,shift()删除数组的第一个元素并返回。

《javascript面向对象精要》总结(一)

关于数据类型

原始类型:boolean,number,string,null,undefined。基础知识就不赘述了,那如何鉴别这些基础类型呢?最好的方法是使用操作符typeof

console.log(typeof "string");
console.log(typeof 10);
//像这样的用法大家都很熟悉,值得注意的是,typeof null,返回的值是"object",这不是我们需要的,所以通常使用value === null来判断值是否为null(注意要使用‘===’,因为undefined==null返回true)。

原始封装类型:String,number,Boolean,不知道你有没有注意到,这些类型和引用类型一样的好用,有他们自己的属性和方法。

var name = "Shi Jun We";
var firstChar = name.charAt(0);
console.log(firstChar);    // "S"

//然后实际上背后发生了这些事情,相信不是很难理解

var name = "Shi Jun wei";
var temp = new String(name);
var firstChar = temp.charAt(0);
temp = null;
console.log(firstChar);
//这种临时对象,只是在值被读取的时候创建。也可以手动创建原始封装类型的数据:
   var name = new String("Shi Jun We");
console.log(typeof name);    //"object"
var found = new Boolean(false);
if(found){
    console.log("Found");
}
//如果你测试了上面的代码,你会发现这样的问题的存在,所以尽量避免这么做。

引用类型:Array,object。引用值是引用类型的实例,引用值就是对象,是一个属性和方法的无序列表。

var object = new Object();
var object = {};

var object2 = object;
object2 = null;

var array = [];
var method = "push";
array[method](12345);

要解除一个对象的引用,,只需要把这个变量的值改成null即可。


关于函数

Javascript函数其实就是一个对象,使函数不同于其他对象的决定特点就是函数存在一个被称为[[call]]的内部属性,typeof会查找这个属性,如果找到回返回function。内部属性是无法通过代码访问的,而是定义了代码执行时的行为。ECMAScript为JavaScript的对象的对象定义了多种内部属性,这些内部属性都用双括号标注。

//你可以尝试如下代码:
var found = new Boolean(false);
console.log(found);        // Boolean {[[PrimitiveValue]]: false}

函数的声明方式有两种,之前的文章有介绍过,这里就不赘述了。

参数对于函数来讲是非常重要,你可以给函数传递任意个数的参数却不造成错误。函数的实际参数保存在arguments的类似数组的对象中。函数的命名参数是为了方便,而不是为了限制参数的个数。

函数期望的(定义的)参数个数保存在函数的length属性中,无论你是以那种方式声明的函数。

var abc = function(a,b,c){};
console.log(abc.length);    // 3

因此,javascript中是不存在重载的,但是我们也可以根据传入的参数来选择不同的行为。可以通过参数的个数(arguments.length)或者判断命名参数是否为undefined来实现。

this我们都已经很熟悉了,javascript所有函数作用域内都有一个this对象代表调用该函数的对象。在全局作用域中,this就代表全局对象window了。至于改变this指向的方法,常用的call(),apply()这里就不多说了,我们来说一下bind()方法:

//bind()的第一个参数是传递给函数的this值,其他所有参数代表需要被永久设置在函数中的命名参数
function sayNameForAll(label){
    console.log(label+':'+this.name);
}

var person1 = {
    name:"Shi"
};
var person2 = {
    name:"Grey"
};

var sayNameForPerson1 = sayNameForAll.bind(person1);
sayNameForPerson1("person1");    // "person1:Shi"

//sayNameForPerson2不仅绑定this为person2,同时也绑定了一个参数为“person2”
var sayNameForPerson2 = sayNameForAll.bind(person2,"person2");
sayNameForPerson2();    // "person2:Grey"

javascript排序算法

冒泡排序

冒泡排序的主要思想是,按照某一顺序(从后向前或从前向后)逐一比较相邻的元素,按照规定的条件交换元素位置,每次循环可以确定一个元素的最终位置,所以冒泡排序在外层共需要数组长度-1次的循环来确定所有元素的正确位置。有如下一个数组:

var arr = [5,7,10,3,4,6,20,9,0];

如下图,我们从后向前循环,第一次循环比较完之后,0会被换到第一个位置上:

以此类推,要进行N-1次的循环,就能完成排序。

function bubbleSort(arr){
  var len = arr.length, i, j;
  // 外层循环次数为数组长度-1
  for(i = len - 1; i >= 1; i--){
      //内层循环交换元素,循环长度最开始为数组长度,依次递减
    for(j = 0; j <= i - 1; j++){
        //满足条件交换位置
      if(arr[j] > arr[j + 1]){
        d = arr[j + 1];
        arr[j + 1] = arr[j];
        arr[j] = d;
      }
    }
  }
  return arr;
}

快速排序

首先选出一个关键值,以这个值为基准,循环遍历数组,将数组分成比关键值大的一组(left)比关键值小的一组(right),之后再递归调用方法,直到传入的数组长度为1时,直接返回,并按照left,关键值,right将数组进行连接,实现排序。
如下图所示:

代码实现如下:

function quickSort(arr){
  if(arr.length <= 1) return arr;
  //关键值索引
  var pivotIndex = Math.floor(arr.length/2)
      // 获取关键值
    , pivot = arr.splice(pivotIndex, 1)[0]
    // 根据关键值分组
    , left = []
    , right = [];
    // 向不同分组中添加元素
  for(var i = 0; i < arr.length; i++){
    if(arr[i] < pivot){
      left.push(arr[i]);
    }else{
      right.push(arr[i]);
    }
  }
  // 递归调用并连接数组
  return quickSort(left).concat([pivot], quickSort(right));
}

归并排序

归并排序首先要将待排序的数组进行分解,分解成多个单元素的数组,之后先进行两两比较,确定顺序后合并,再将合并后的数组进行两两比较,确定顺序并合并,直到所有数组合并完成。如下图:

分组:

归并排序:

代码实现:

function mergeSort (items) {
    //递归分组,分组到每组只有一个元素,返回该数组
    if(items.length==1){
        return items;
    }
    var middle=Math.floor(items.length/2),
    left=items.slice(0,middle),
    right=items.slice(middle);
    return merge(mergeSort(left),mergeSort(right));
}

function merge (left,right) {
    //排序,先比较一个值得数组之间的大小,并进行排序,之后递归结果
    var result=[];
    while(left.length>0 && right.length>0){
        //单个元素时只比较0位置的元素,多个时是已经排好顺序的,所以也
        if(left[0]<right[0]){
        /*shift()方法用于把数组的第一个元素从其中删除,并返回第一个元素的值。*/
            result.push(left.shift());
        }else{
            result.push(right.shift());
        }
    }
    return result.concat(left).concat(right);
}

用canvas制作炫酷的倒计时

利用canvas绘制炫酷的倒计时器

之前无意间看到了利用canvas制作定时器的例子,感觉还是挺不错的,跟着敲了一下,分享出来看看!

年,月,日,时:分:


当前浏览器不支持Canvas,请更换浏览器后再试

View On GitHub