<script>
</script>
<script src="./xx.js">
</script>
数值类: Number:包含浮点数,负数 javascript
NAN:不是数 使用isNan函数来判断是否为NAN css
Infinity无限大 html
字符串:以单引号或者双引号括起来的任意文本 java
布尔值: || && node
比较运算符: == 比较时自动进行类型转换,比较诡异,最好不要用 === 比较时不会进行类型转换 web
常识:ajax
浮点数的比较由于精度问题不会相等 正则表达式
(1/3) === (1-2/3) //false chrome
缘由:浮点数的精度表示,应用Math.abs(1/3-(1-2/3))<0.0000001 json
null: 空 注意不是数字0 也不是空字符串“”
undefine: 未定义,在判断是否传入了参数的时候用 数组:
[]或者new Array(1,2,3);可经过[索引号]来访问 用Array.isArray来判断是否为数组
对象:一组键值对构成想无序集合,能够.或者[索引名]来访问属性
变量名的规则:大小写英文字母,$ 与_构成的集合,不能以数字开头,不能与关键字冲突。
赋值:= 声明一个变量用var,不用var表示全局,在不一样的js文件中冲突后会形成覆盖影响。
${变量名}访问变量
属性:length 直接给length赋值会致使Array的大小发生变化。
索引赋值:var arr=[1,2,3]; arr[5]=‘x’; 此时arr变成[1,2,3,undefine,undefine,‘x’]
常见方法:
对象能够视为Map或者dictionary的一种表示,即键值对。 对象中的属性key必须为字符串,实际上为Number或其余类型也能够,故引入map
方法:
set为不可重复的key的集合,用add添加,delete删除
iterable 适用于Array map set
1.可用for of来遍历//此方法只遍历属于集合或字典自己的元素
例子:for (var x in arr)的缺点,for of 对此的改进
例如:var a=[1,2,3]; var a.name=‘hello’; for (var x in a){alert(x)};//会打印 ‘0’ ‘1’ ‘2’ ‘hello’forEach方法—iterable内置的方法,传入一个函数,每次迭代都执行该回调函数
例如:iterable对象.forEach(function(element,index,this){})//对于set element和index为同一个元素,由于它没有索引。一个函数参数为函数,此函数为高阶函数。var a =['A','B','C']; for (var i in a ){alert(i); alert(a[i]);
//过滤到继承的属性用hasOwnProperty
对于数字、字符串等是将它们的值传递给了函数参数,函数参数的改变不会影响函数外部的变量。
对于数组和对象等则是将对象(数组)的变量的值传递给了函数参数,这个变量保存的指向对象(数组)的地址。当函数改变这个地址指向的对象(数组)的内容时,同时也改变了函数外部变量指向的对象(数组)的内容;当函数改变的是变量的地址时,实际就与函数外部的变量失去了联系,变成了彻底不一样的对象了,不会对函数外部对象形成改变。
var v1 = [] var v2 = {}; var v3 = {}; function foo(v1, v2, v3)//这里分别是给v1 v2 v3 赋值新对象,对外部的对象不会有改变 { v1 = [1]; v2 = [2]; v3 = {a:3} } foo(v1, v2, v3); alert (v1); // 空白 alert (v2); // [object Object] alert (v3.a); // undefined
var v1 = [] var v2 = {}; var v3 = {a:0}; function foo(v1, v2, v3)//这里不是赋值新对象,而是直接操做它,会形成函数外部对象改变 { v1.push (1); v2.a = 2; v3.a = 3; } foo(v1, v2, v3); alert (v1); // 1 alert (v2.a); // 2 alert (v3.a); // 3
定义:
1. function fun_name(prama1,prama2){…}
2. var var_name = function (prama1,prama2){};//注意最后有一个分号
方法2存在变量提高问题:
例如:
1. alarm(1); function alarm(){}
2. alarm(1); var alarm= function (){}//此处由于变量提高,实际顺序为 var alarm; alarm(1); alarm = function(){};//会在alarm(1)那句报错,alarm is undefine
函数结束的两种状况:
1. return
2. 执行到末尾,无return,返回undefine
参数传递:
arguments关键字 像Array,实际不是Array。其属性length存着参数个数。
..rest关键字 若是参数过多,用for循环一个个取的话比较麻烦
例如:
function (a,b ){ var i,var rest=[]; if(arguments.length>2) { for(i=2;i<arguments.length;i++) rest.push(arguments[i]) } }
function(a,b,...rest){}
返回值坑:
return 多行
return {
{name:'foo'}
{
var x = 1;
function bar()
{
var y = x+1;
}
var z = y+1;//reference Error
} * 变量提高,是由变量的查找机制引发的。 ‘use strict’
function foo()
{
var x = ‘hello ’+y;
alert(x);//打印为 hello undefine
var y = ‘bob’;
}
因为变量的查找机制,如今函数内查找,找到了,因而至关于这样的顺序 var y ; var x =“hello”+y; y=‘bob’。
如何简单理解? 将内部全部var 定义都放在函数体前面。
名字空间:全局变量会绑定到window上,相同的全局变量会冲突
减小冲突的方法把本身用的全部变量和函数所有绑定到一个全局变量中
例如:
var MYAPP={};//MYAPP在这里叫名字空间 JQUERY YUI underscore都是这么干的
MYAPP.name =“myapp”;
MYAPP.version = 1.0;
MYAPP.foo = function (){};
局部做用域:默认做用域为函数内部
‘use strict’
function()
{
for(var i =0;i<100;i++){;}
i+=100;//仍然能够引用该变量
}
常量
曾经:var PI = 3.14 ;//用大写变量名表示不要修改它
如今可用const,和let同样具备块级做用域。
例如:const P1=3.14; 修改不报错,可是没有效果
什么是高阶函数?
一个函数的参数为一个函数。例如:
function add(x,y,f) { return f(x)+f(y); }
map
有一个函数f(x)= x2,做用在[1,2,3,4,5,6,7,8,9]上,map的实现以下:
实现:
function pow(x) { return x*x; } var arr=[1,2,3,4,5,6,7,8,9]; arr.map(pow);
等价形式:
for(var i =0 ;i<arr.length;i++){//此代码缺点:不易读懂,而map能够一行搞定 result.push(pow(arr[i])); }
reduce
Array.reduce方法在[x1,x2,x3,x4]上,效果是:
[x1, x2, x3, x4].reduce(f) = f(f(f(x1, x2), x3), x4);
用例:对一个Array求和的reduce实现
var arr=[1,3,5,7,9]; arr.reduce(function(x,y){ return x+y; });
filter
filter与map相似,但其是根据return 的false true来决定每一个元素的去留。map reduce filter 对undefine的处理?对map来讲undefine仍是undefine 对filter不理不理undefine
sort:对Array直接修改,返回的是同一个Array
默认是按照ASCII码排序
用例:
['Google','Apple','Microsoft'].sort();//['Apple','Google','Microsoft'] ['Google','Apple','Microsoft'].sort();//['Google','Microsoft','apple'] [10,20,1,2].sort();//[1,10,2,20]为何?先转化为字符串再string排序
如何实现对数字排序?
传入排序方法函数
var arr=[10,20,1,2]; arr.sort( function(x,y) { if(x>y) return 1;//将x后移 实现升序排列if(x<y) return -1; return 0; } );
什么是闭包,与高阶函数类似的概念
高阶函数指接受一个函数做为参数的函数。一样函数也能够做为返回值,就叫闭包。
function sum(arr) { return arr.reduce( function(x,y) { return x+y; } ); } var f=sum([1,2,3,4,5]);//f接受一个函数 f();//调用,才是真正计算结果。
内部函数sum能够引用外部函数的参数和局部变量,相关参数和变量都般存在返回的函数中,称为“闭包”。
每次调用都产生一个新函数,即便参数相同。
闭包的用途
私藏变量:
用例:
function count() { var arr =[]; for(var i=1;i<=3;i++) { arr.push(function(){return i*i;}); } return arr; } var result = count(); result.map(funciton(x){console.log(x());})
//会打印16 16 16 为何不是1 4 9
因为变量的查找机制,arr中function没有找不到i的定义,因而向外层函数找,找到时此变量值为4。
返回函数不要引用任何循环变量,或者后续会发生变化的量
必定要引用循环变量怎么办,用闭包私藏起来,方法:建立一个匿名函数并当即执行,匿名函数引用变量并返回内部函数
function count() { var arr =[]; for(var i=1;i<=3;i++) { arr.push( (function (n){ return function(){return n*n;}; })(i);//建立匿名函数并当即执行,匿名函数参数为i ); } return arr; }
建立匿名函数名当即执行:
function (x) {return x*x;}(x)
//理论上的写法;但与函数体的定义冲突,Syntax Error (function (x) {return x*x;})(x)
//给函数的定义加上括号
私藏的变量,即时是外部也没法访问,并不与函数对象的原变量并非同一个。例子:
'use strict'function create_counter(initial) { var x = initial||0; return {//返回一个对象 inc:function(){ x+=1; return x; } } } //使用var c1= create_counter(); c1.inc();//1 c1.inc();//2
对于每一个返回的函数,其外部函数参数和局部变量,都是独立的,而且相对于create_count.x也是独立的
封装函数
用例:常用x2和x3,可生成两个函数,能够简化后期的参数输入
function make_pow(n) { return function(x){ return Math.pow(x,n); }; } var pow2=make_pow(2); var pow3=make_pow(3); pow2(5);//25 pow3(7);//343
至关于匿名函数,当仅有一个表达式的时候,连{}和return 都省略了;有多条语句,就不能省略{}和return了。
f= x=> x*x; x=>{ if(x>0) return x*x; elsereturn -x*x*x; } (x,y)=>x*x+y*y;//两个参数 ()=>3.14;//无参数 (x,y,...rest)=>{};//可变参数 x=>{foo:x};//返回一个对象//由于和函数体的定义冲突,须要改成 x=>({foo:x})
箭头函数能够解决函数内部定义函数this指向问题
var obj={ birth:1990, getAge: function () { var b = this.birth; // 1990
var fn = function () { return new Date().getFullYear() - this.birth; // this指向window或undefined }; return fn(); } }改进方法1 bind
getAge: function () { var b = this.birth; // 1990
var fn = function () { return new Date().getFullYear() - this.birth; // this指向window或undefined }; return fn().bind(this); }改进方法2 => :此时that就不须要了
var fn=()=>new Date().getFullYear()-this.birth;
//this此时指向window或undefine(strict模式下);
什么是生成器? 标志是 函数名前面有* 里面有yield
可在执行过程当中返回屡次,像一个能够记住状态的函数。生成器函数返回的是一个生成器对象。
function * fab(max){ var t,a=0,b=1,n=1; while(n<max) { yield a; t=a+b; a=b; b=t; n++; } return a; }
var f = fab(5); f.next();//{value:0,done:false} f.next();//{value:0,done:false} f.next();//{value:0,done:true}//next方法须要咱们根据返回对象的done属性判断是否完成。
for (var x of fab(3))//用该方法不用判断返回对象的done属性
生成器的用途 解决回调地狱
ajax('http://url-1', data1, function (err, result) { if (err) { return handle(err); } ajax('http://url-2', data2, function (err, result) { if (err) { return handle(err); } ajax('http://url-3', data3, function (err, result) { if (err) { return handle(err); } return success(result); }); }); });上面的写法回调越多,越难看
try { r1 = yield ajax('http://url-1', data1); r2 = yield ajax('http://url-2', data2); r3 = yield ajax('http://url-3', data3); success(r3); } catch (err) { handle(err); } //看上去同步的代码,其实是异步的。只是写法上的改变而已。yield与generator(*)结构,结合Promise的then catch,改善了异步回调的写法,Async await生成方法被称为改善回调地狱的最后亮光。
var xiaoming = { name:"小明",'middle-school':'No.1 middle school'};//最后一个属性不要加,
访问属性用.或者[‘属性名’] 注意middle-school属性并不是变量名,只能用xiaoming['middle-school]的方式来访问。
检测是否有某属性/方法用in 。 ‘name’ in xiaoming ; ‘toString’ in xiaoming; toString定义在Object中,xiaoming的原型链指向Object。
检测时自身拥有的属性,仍是继承来的用hasOwnProperty方法
什么叫方法?将一个函数绑定到一个对象
例1:
var xiaoming ={ name:"小明", birth:1990; age:function(){ var y = new Date().getFullYear(); return y -this.birth; } }
执行xiaoming.age()//里面的this指向xiaoming
例2://分开写
function getAge(){ var y = new Date().getFullYear(); return y -this.birth; } var xiaoming ={ name:"小明", birth:1990; }
执行xiaoming.getAge()//里面的this指向xiaoming 执行getAge() //返回NAN this指向window
例3:
'use strict'var xiaoming ={ name:"小明", birth:1990; age:function(){ var y = new Date().getFullYear(); return y -this.birth; } } var fn = xiaoming.age(); fn();//非strict下指向window,strict下指向undefine
例4:
var xiaoming ={ name:"小明", birth:1990; age:function(){//函数内部定义函数this的问题function getAgeFromBirth() { var y = new Date().getFullYear(); return y-this.birth; } return getAgeFromBirth(); } }
执行xiaoming.age()//里面的this非strict下指向window,strict下指向undefine。
修正:
var xiaoming ={ name:"小明", birth:1990; age:function(){//函数内部定义函数this的问题 that = this; function getAgeFromBirth() { var y = new Date().getFullYear(); return y-that.birth; } return getAgeFromBirth(); } }
对于函数内定义的函数,this非strict下指向window,strict下指向undefine。
如何控制this的指向? call apply bind
使用方式:
装饰器模式的实现 使用apply实现
例如:统计一下使用了多少次parseInt
var count = 0; var oldparseInt = pareseInt;//保存原函数 window.parseInt = function () { count+=1; return oldparseInt.apply(null,arguments); } parseInt('10'); parseInt('20'); parseInt('30');
typeof 123;//'number'
typeof NaN;//'number'
typeof 'str';//'string'
typeof true;//'boolean'
typeof undefine;//'undefined'
typeof Math.abs;//'function'
typeof null;//'object'
typeof [];//'object'
typeof {};//'object'
typeof new String('str');//'object'new String('str') ==='str'//false不要使用包装对象
总结:
不要使用new Number()、new Boolean()、new String()建立包装对象;
用parseInt()或parseFloat()来转换任意类型到number;
用String()来转换任意类型到string,或者直接调用某个对象的toString()方法;
一般没必要把任意类型转换为boolean再判断,由于能够直接写if (myVar) {…};
typeof操做符能够判断出number、boolean、string、function和undefined;
判断Array要使用Array.isArray(arr);
判断null请使用myVar === null;
判断某个全局变量是否存在用typeof window.myVar === ‘undefined’;
函数内部判断某个变量是否存在用typeof myVar === ‘undefined’。
最后有细心的同窗指出,任何对象都有toString()方法吗?null和undefined就没有!虽然null还假装成了object类型。
更细心的同窗指出,number对象调用toString()报SyntaxError:
123.toString(); // SyntaxError
遇到这种状况,要特殊处理一下: 123..toString(); // ‘123’, 注意是两个点! 由于Number类型有浮点数 (123).toString(); // ‘123’
var now = new Date(); now;//var now = Date(); now;//wed jun 24 2015 19:49:22 GMT +800(CST) now.getFullYear();//2015 now.getMonth();//0-11 now.getDate();//24 24号 now.getDay();//星期 这个比较特别 其余的都是年月日时分秒毫秒 now.getHours();//24小时制 now.getMinutes();//49分钟 now.getSeconds();//秒 now.getMilliseconds();//毫秒数 now.getTime();//时间戳 13位 以Number形式,表示1970年1月1日0点整通过的毫秒数,在GMT时区下
new Date(2015,5,19,20,3,3,123);//年月日时分秒毫秒时区
var d = Date.parse('2015-06-24T19:24:22.876+0800');//T后面接时间 +/-hh:mm表示相对于UTC超前/滞后的时间
var time = Date(d);//将时间戳转化为人能够读懂的时间 now.toLocaleString();//转换为当地时间 now.toTimeString();//UTC+8为中国时间
时区知识:GMT 格林威治时间 UTC世界协调时间 DST夏日节约时间 CST同时表示4个时区,用+hh:mm表示区别
匹配字符串强有力的武器,凡是符号规则的字符串。
入门:
直接给出某字符为精确匹配
\d 匹配一个数字
\w一个字母或数字或与[a-zA-Z0-9]等效
. 任意一个字符
变长任意个字符
至少一个字符
? 0个或1个字符
{n} n个字符
{n,m} n-m个字符
例子: \d{3}\S+\d{3,8}
\d{3}表示匹配3个数字
\S+ 表示至少一个空白字符(空格 tab等)
\d{3,8}表示3-8个数字
\d{3}\-\d{3,8} 能够匹配010-1235
-是特殊字符须要转义
进阶:
[]表示字符范围,如[0-9a-z]
[]可用A|B,表示匹配A或者B
表示行头,在[]中表示非 ^\d必须以数字开头
$表示行尾
\d$必须以数字结尾
正则表达式的建立
/正则表达式/ 去掉里面的正则表达式,则变成了行注释的符号
new RegExp(‘正则表达式’); ‘ABC\-001’ \实际为一个,须要转义 用途: 1.测试给定的字符串是否符合条件 2.用于切分字符串 split
'ab c'.split('');//['a','b',' ',' ','c']存在多个空格符怎么办
'ab c'.split(/\s+/);//['a','b',' ',' ','c'] 存在","怎么办?
'a,b, c'.split(/[\s,]+/);//['a','b',' ',' ','c'] 存在";"呢?
'a,b; c'.split(/[\s,;]+/);//['a','b',' ',' ','c']
相关函数: match一个或多个,返回数组 search 第一个 replace split
分组和提取子串 子表达式的反向引用\2 \1
var re = /^(\d{3}-(\d{3,8}))/; re.exec('010-12345');//['010-12345','010','12345']; re.exec('010 12345');//null
/(\d)(\d)\2\1/;//能够匹配5775 1221 之类的字符串 \2 反向引用子式2 \1反向引用子式1
全局搜索 g(可执行屡次匹配,不加指匹配一次) i(忽略大小写) m多行匹配(影响多行中^$的匹配)
var r1=/test/g;//或者 var r1=new RegExp(‘test’,‘g’);
可执行屡次exec,每次exec会更新lastIndex属性 lastIndex leftcontent rightcontent
var s = 'JavaScript, VBScript, JScript and ECMAScript'; var re=/[a-zA-Z]+Script/g; // 使用全局匹配: re.exec(s); // ['JavaScript'] re.lastIndex; // 10 re.exec(s); // ['VBScript'] re.lastIndex; // 20 re.exec(s); // ['JScript'] re.lastIndex; // 29 re.exec(s); // ['ECMAScript'] re.lastIndex; // 44 re.exec(s); // null,直到结束仍没有匹配到
正则表达式的局限:像‘2-30’ ‘4-31’这样的非法日期,用正则表达式是识别不了或者实现困难,要配合程序来处理
正则表达式的贪婪匹配:
var re=/^(\d+)(0*)/; re.exec('102300');//["102300","102300",""]//加个?表示非贪婪匹配 re=/^(\d+?)(0*)/; re.exec('102300');//["102300","1023","00"] re=/1{3}/;//匹配11111111 会获得 111 111 第一次匹配会更新lastIndex属性
元字符的分类:1.限定次数符 {n} {n,m} + * ? 2.选择匹配符 3.分组组合与反向引用 4.特殊字符 5.字符匹配符 6.定位符
JSON以前一直使用XML,犹豫XML规范众多,Douglas发明了JSON。
JSON中一共有number、boolean、string、null、array([])、object({…}),并规定死了字符集为UTF-8,规定字符串必须用“”,
object的键也必须用“”。
方法:
序列化—字符串化
使用JSON类的stringify(对象,筛选属性数组或一个函数,每一个键值对都会被函数先处理,缩进)。
写出对象的TOJSON方法,返回JSON应该序列号的数据
反序列化—将其转换为对象
使用eval执行表达式,太过危险
JSON.parse(字符串,函数) 传入函数会处理键值对
javascript不区分类和实例的概念,而是经过原型(prototype)来实现。
var Student = { name: 'Robot', height: 1.2, run: function () { console.log(this.name + ' is running...'); } }; var xiaoming = { name: '小明' }; xiaoming.__proto__ = Student; //把xiaoming的原型指向了对象Student,看上去xiaoming仿佛是从Student继承下来的
javascript全部对象都是实例,继承是把一个对象的原型指向另外一个对象。 实际中不要使用__proto__
属性改变一个对象原型,
可经过编写函数配合Object.create(传入原型)的方法:
例如:
// 原型对象:var Student = { name: 'Robot', height: 1.2, run: function () { console.log(this.name + ' is running...'); } }; function createStudent(name) { // 基于Student原型建立一个新对象:
var s = Object.create(Student); // 初始化新对象: s.name = name; return s; } var xiaoming = createStudent('小明'); xiaoming.run(); // 小明 is running... xiaoming.__proto__ === Student; // true
每建立一个对象都会设置一个原型
属性/方法访问机制
当访问obj.xxx属性时,如今当前对象查找属性,没找到,再到原型对象找,还未找到,再到Object.prototype对象,
若是仍然没有找到,放回undefine
原型链
var arr=[1,2,3]; //原型链为 arr->Array.prototype->object.prototype->nullfunction foo(){return 0;} //原型链为 foo->Function.prototype->object.prototype->null//Function中有Apply方法
使用构造函数建立对象
function Student(name) {//注意建立对象的函数首字母为大写,普通函数为小写this.name = name; this.hello = function () { alert('Hello, ' + this.name + '!'); } } var xiaoming = new Student(' 小明'); //写new的做用 函数会变成构造函数: 1. 内部this指向新建立的对象 2 默认返回this
//不写new 返回undefine
若是还有别的对象,其原型链为:
xiaoming ↘
xiaohong -→ Student.prototype —-> Object.prototype —-> null
xiaojun ↗
xiaoming.constructor === Student.prototype.constructor; // true Student.prototype.constructor === Student; // true Object.getPrototypeOf(xiaoming) === Student.prototype; // true xiaoming instanceof Student; // true
//函数Student刚好有个属性prototype指向xiaoming、xiaohong的原型对象
//可是xiaoming、xiaohong这些对象可没有prototype这个属性,
//不过能够用__proto__这个非标准用法来查看。
如何共享同一个函数,而不是每一个对象各有一份代码?
xiaoming.hello === xiaohong.hello;//false
将函数写入prototype,修改代码以下:
function Student(name) { this.name = name; } Student.prototype.hello = function () { alert('Hello, ' + this.name + '!'); };//注意此处有分号
构造函数忘记写new会怎样?
在strict模式下,this执行undefine,非stric模式下,this执行window,会建立全局变量;jslint能够检查出漏泄的new
如何防止漏写new? 编写函数,将new封到函数里面
function Student(props) { this.name = props.name || '匿名'; // 默认值为'匿名'
this.grade = props.grade || 1; // 默认值为1 } Student.prototype.hello = function () { alert('Hello, ' + this.name + '!'); }; function createStudent(props) { return new Student(props || {}) } var xiaoming = createStudent({//传入参数 name: '小明' }); //好处 1.不用new来调用 2.传入参数灵活,因为参数是对象,咱们能够把JSON转为对象直接传入。 xiaoming.grade; // 1 修改参数
基于上街Student扩展出PrimaryStudent,能够先定义出PrimaryStudent:
function PrimaryStudent(props) { // 调用Student构造函数,绑定this变量: Student.call(this, props); this.grade = props.grade || 1; }
调用了Student构造函数不等于继承了Student,PrimaryStudent建立的对象的原型是:
new PrimaryStudent() —> PrimaryStudent.prototype —> Object.prototype —> null
必须想办法把原型链修改成:
new PrimaryStudent() —> PrimaryStudent.prototype —> Student.prototype —>Object.prototype —> null
PrimaryStudent对象既能够调用PrimaryStdent.prototype定义获得方法,也能够调用原型链上的方法。
若是你想用最简单粗暴的方法这么干:
PrimaryStudent.prototype = Student.prototype;
是不行的!若是这样的话,PrimaryStudent和Student共享一个原型对象,那还要定义PrimaryStudent干啥?
咱们必须借助一个中间对象来实现正确的原型链,这个中间对象的原型要指向Student.prototype。
为了实现这一点,参考道爷(就是发明JSON的那个道格拉斯)的代码,中间对象能够用一个空函数F来实现:
// PrimaryStudent构造函数:function PrimaryStudent(props) { Student.call(this, props); this.grade = props.grade || 1; } // 空函数F:function F() { } // 把F的原型指向Student.prototype: 1.先指明F对象建立模板为Student,F内才能找到Student对象的属性 F.prototype = Student.prototype; // 把PrimaryStudent的原型指向一个新的F对象,F对象的原型正好指向Student.prototype: PrimaryStudent.prototype = new F();//2// 把PrimaryStudent原型的构造函数修复为PrimaryStudent: PrimaryStudent.prototype.constructor = PrimaryStudent;//3
// 继续在PrimaryStudent原型(就是new F()对象)上定义方法: PrimaryStudent.prototype.getGrade = function () { return this.grade; }; // 建立xiaoming:var xiaoming = new PrimaryStudent({ name: '小明', grade: 2 }); xiaoming.name; // '小明' xiaoming.grade; // 2// 验证原型: xiaoming.__proto__ === PrimaryStudent.prototype; // true xiaoming.__proto__.__proto__ === Student.prototype; // true// 验证继承关系: xiaoming instanceof PrimaryStudent; // true xiaoming instanceof Student; // true
进一步封装
若是把继承这个动做用一个inherits()函数封装起来,还能够隐藏F的定义,并简化代码:
function inherits(Child, Parent) {//此函数能够复用
var F = function () {}; F.prototype = Parent.prototype; Child.prototype = new F(); Child.prototype.constructor = Child; }
使用:
function Student(props) { this.name = props.name || 'Unnamed'; } Student.prototype.hello = function () { alert('Hello, ' + this.name + '!'); } function PrimaryStudent(props) { Student.call(this, props); this.grade = props.grade || 1; } // 实现原型继承链: inherits(PrimaryStudent, Student); // 绑定其余方法到PrimaryStudent原型: PrimaryStudent.prototype.getGrade = function () { return this.grade; };
JavaScript的原型继承实现方式就是:
定义新的构造函数,并在内部用call()调用但愿“继承”的构造函数,并绑定this;
借助中间函数F实现原型链继承,最好经过封装的inherits函数完成;
继续在新的构造函数的原型上定义新方法。
在上面的章节中咱们看到了JavaScript的对象模型是基于原型实现的,特色是简单,缺点是理解起来比传统的类-实例模型要困难,
最大的缺点是继承的实现须要编写大量代码,而且须要正确实现原型链。
有没有更简单的写法?有!
新的关键字class从ES6开始正式被引入到JavaScript中。class的目的就是让定义类更简单。
咱们先回顾用函数实现Student的方法:
function Student(name) { this.name = name; } Student.prototype.hello = function () { alert('Hello, ' + this.name + '!'); }
若是用新的class关键字来编写Student,能够这样写:
class Student { constructor(name) { this.name = name; } hello() { alert('Hello, ' + this.name + '!'); } }
比较一下就能够发现,class的定义包含了构造函数constructor和定义在原型对象上的函数hello()(注意没有function关键字),
这样就避免了Student.prototype.hello = function () {…}这样分散的代码。
最后,建立一个Student对象代码和前面章节彻底同样:
var xiaoming = new Student('小明'); xiaoming.hello();
class继承
用class定义对象的另外一个巨大的好处是继承更方便了。想想咱们从Student派生一个PrimaryStudent须要编写的代码量。
如今,原型继承的中间对象,原型对象的构造函数等等都不须要考虑了,直接经过extends来实现:
class PrimaryStudent extends Student { constructor(name, grade) { super(name); // 记得用super调用父类的构造方法!this.grade = grade; } myGrade() { alert('I am at grade ' + this.grade); } }
注意PrimaryStudent的定义也是class关键字实现的,而extends则表示原型链对象来自Student。子类的构造函数可能会与父类不太相同,
例如,PrimaryStudent须要name和grade两个参数,而且须要经过super(name)来调用父类的构造函数,不然父类的name属性没法正常初始化。
PrimaryStudent已经自动得到了父类Student的hello方法,咱们又在子类中定义了新的myGrade方法。
ES6引入的class和原有的JavaScript原型继承有什么区别呢?实际上它们没有任何区别,class的做用就是让JavaScript引擎去实现原来须要咱们
本身编写的原型链代码。简而言之,用class的好处就是极大地简化了原型链代码。
你必定会问,class这么好用,能不能如今就用上?
如今用还早了点,由于不是全部的主流浏览器都支持ES6的class。若是必定要如今就用上,就须要一个工具把class代码转换为传统的prototype代码,
能够试试Babel这个工具。
现有的浏览器内核与javascript引擎
window 对象不但充当全局做用域,并且表示浏览器窗口。
navigator对象表示浏览器的信息,最经常使用的属性包括:
var width= window.innerWidth||document.body.clientWidth;
screen对象表示屏幕的信息,经常使用的属性有:
location对象表示当前页面的URL信息。例如,一个完整的URL:http://www.example.com:8080/path/index.html?a=1&b=2#TOP
属性:
方法:
document对象表示当前页面。因为HTML在浏览器中以DOM形式表示为树形结构,document对象就是整个DOM树的根节点。
属性:
title 是从title标签内容中读取的,能够动态修改。
document.title = ‘努力学习JavaScript!’;
cookie
Cookie是由服务器发送的key-value标示符。由于HTTP协议是无状态的,可是服务器要区分究竟是哪一个用户发过来的请求,就能够用Cookie来区分。
当一个用户成功登陆后,服务器发送一个Cookie给浏览器,例如user=ABC123XYZ(加密的字符串)…,此后,浏览器访问该网站时,会在请求头附上这个Cookie,服务器根据Cookie便可区分出用户。
javascript可经过document.cookie来读取到当前页面的cookie。若是浏览器嵌入了第三方的javascript,会形成巨大隐患。
服务器在设置cookie时可使用httponly这样的选项,这样的cookie就不能经过javascript代码读取了。
获取节点的方法
var menu = document.getElementById(''); menu.tagName;//输出标签名var drinks = document.getElementByTagName('');
history
方法:
实现无刷新更换URL地址
window.history.pushState({},0,url);
利用ajax配合pushState翻页无刷新的动做
因为HTML文档被浏览器解析后就是一棵DOM树,要改变HTML的结构,就须要经过JavaScript来操做DOM。
树形结构:
更新:更新该DOM节点的内容,至关于更新了该DOM节点表示的HTML的内容;
遍历:遍历该DOM节点下的子节点,以便进行进一步操做;
添加:在该DOM节点下新增一个子节点,至关于动态增长了一个HTML节点;
删除:将该节点从HTML中删除,至关于删掉了该DOM节点的内容以及它包含的全部子节点。
遍历获取DOM节点—在操做一个DOM节点前,咱们须要经过各类方式先拿到这个DOM节点。
经过document的方法和属性
[索引号]
[索引号]
得到某节点下的全部直属点
经过属性:
经过选择器方法querySelector 和QuerySelectorAll() 须要了解select的用法,相似于JQUERY 的选择器。
node和element的区别?
node更为广义,包括了element、comment、CDATA_SECTION等多种,但绝大时候,咱们只关心element,由于他是能够看到的,comment这种是不可见的。
插入 若是dom节点是空的,能够经过innetHTML来写
appendChild 插入到父节点的最后一个节点。例如:
var d = document.createElement('style'); d.setAttribute('type', 'text/css'); d.innerHTML = 'p { color: red }'; document.getElementsByTagName('head')[0].appendChild(d);
parentElement.insertBefore(newElement,referenceElement) 插入到参照节点前
haskell = document.createElement('p'); haskell.id = 'haskell'; haskell.innerText = 'Haskell'; list.insertBefore(haskell, ref);
删除—要删除一个节点,首先要得到该节点自己以及它的父节点,而后,调用父节点的removeChild把本身删掉:
removeChild
删除后的节点虽然不在文档树中了,但其实它还在内存中,能够随时再次被添加到别的位置。
children属性是一个只读属性,而且它在子节点变化时会实时更新。更新—也就是修改
<span>
’被编码为‘<span>
’保证其不会产生任何HTML标签。
修改CSS,经过节点的style属性,因为CSS容许font-size并不是javascript有效的属性名,须要将其改成驼峰式,fontSize
p.style.fontSize='20px';
表单为能够得到用户输入的内容,或者对输入框设置新的内容。
表单控件
文本框,对应的<input type="text">
,用于输入文本;
口令框,对应的<input type="password">
,用于输入口令;
单选框,对应的<input type="radio">
,用于选择一项;同一组单选框name属性必须一致
复选框,对应的<input type="checkbox">
,用于选择多项;同一组复选框name必须一致
下拉框,对应的<select>
<option value=""></option>
,用于选择一项;
隐藏文本,对应的<input type="hidden">
,用户不可见,但表单提交时会把隐藏文本发送到服务器。
获取表单值的方式
为输入框设置label标签的函数,单击该处文本,焦点会自动跑到对应输入框上。增大了可单击区域。
// <label><input type="radio" name="weekday" id="monday" value="1"> Monday</label>
// <label><input type="radio" name="weekday" id="tuesday" value="2"> Tuesday</label>
var mon = document.getElementById('monday'); var tue = document.getElementById('tuesday'); mon.value; // '1' tue.value; // '2' mon.checked; // true或者false tue.checked; // true或者false
设置表单值的方式
HTML5表单控件
<input type="date" value="2015-07-01">
日期
<input type="datetime-local" value="2015-07-01T02:03:04">
日期和本地时间
<input type="color" value="#ff0000">
颜色选择器
对于不支持的浏览器会看成 type=“text”来显示。
提交表单的两种方式
<button type="submit">
时提交表单,或者用户在最后一个输入框按回车键。须要使用form的onSubmit属性 <!-- HTML -->
<form id="test-form" onsubmit="return checkForm()">
<input type="text" name="test">
<button type="submit">Submit</button>
</form>
<script> function checkForm() { var form = document.getElementById('test-form'); // 能够在此修改form的input...// 继续下一步:return true; //return flase则不会提交 } </script>
防止提交表单时输入框闪烁 在检查和修改<input>
时,要充分利用<input type="hidden">
来传递数据。
例如,不少登陆表单但愿用户输入用户名和口令,可是,安全考虑,提交表单时不传输明文口令,而是口令的MD5。
普通JavaScript开发人员会直接修改<input>
:
这个作法看上去没啥问题,但用户输入了口令提交时,口令框的显示会忽然从几个变成32个(由于MD5有32个字符)。
要想不改变用户看到这个现象,能够利用<input type="hidden">
实现:
<form id="login-form" method="post" onsubmit="return checkForm()">
<input type="text" id="username" name="username">
<input type="password" id="input-password">
<input type="hidden" id="md5-password" name="password">
<button type="submit">Submit</button>
</form>
<script> function checkForm() { var input_pwd = document.getElementById('input-password'); var md5_pwd = document.getElementById('md5-password'); // 把用户输入的明文变为MD5: md5_pwd.value = toMD5(input_pwd.value); // 继续下一步:return true; } </script>
在HTML表单中,能够上传文件的惟一控件就是<input type="file">
。
注意:当一个表单包含<input type="file">
时,表单的enctype必须指定为multipart/form-data,method必须指定为post,
浏览器才能正确编码并以multipart/form-data格式发送表单的数据。
出于安全考虑,浏览器只容许用户点击<input type="file">
来选择本地文件,用JavaScript对<input type="file">
的value赋值是没有任何效果的。
当用户选择了上传某个文件后,JavaScript也没法得到该文件的真实路径。
一般,上传的文件都由后台服务器处理,JavaScript能够在提交表单时对文件扩展名作检查,以便防止用户上传无效格式的文件:
var f = document.getElementById('test-file-upload'); var filename = f.value; // 'C:\fakepath\test.png'
if (!filename || !(filename.endsWith('.jpg') || filename.endsWith('.png') )) { alert('Can only upload image file.'); return false; }
因为JavaScript对用户上传的文件操做很是有限,之前都是用flash来上传,能够获得上传进度条等信息。
HTML5新增的File API容许JavaScript读取文件内容,得到更多的文件信息。 可经过name size lastModifiedDate获取信息。
// 读取文件:var reader = new FileReader(); reader.onload = function(e) {//回调函数,不知道何时读完,读完后自动调用咱们设置的函数var data = e.target.result; // 'data:image/jpeg;base64,/9j/4AAQSk...(base64编码)...' preview.style.backgroundImage = 'url(' + data + ')'; }; // 以DataURL的形式读取文件: reader.readAsDataURL(file);//以Base64方式,经常使用于设置图像
提交表单时会刷新页面,告诉你操做成功/失败,因为网络太慢或者其余缘由,可能获得404。
web的运做原理:一次HTTP请求对应一个页面 如何让用户停留在该页面,用javascript发送请求并接受数据,更新页面—Ajax
var request; if (window.XMLHttpRequest) {//经过该属性肯定是否支持标准XMLHttpRequest
// 不要判断navigator.userAgent 1.能够伪造 2.判断IE版本复杂 request = new XMLHttpRequest(); } else { request = new ActiveXObject('Microsoft.XMLHTTP'); } request.onreadystatechange = function () { // 状态发生变化时,函数被回调
if (request.readyState === 4) { // 成功完成// 判断响应结果:
if (request.status === 200) { // 成功,经过responseText拿到响应的文本:
return success(request.responseText);
} else { // 失败,根据响应码判断失败缘由:
return fail(request.status);
} } else { // HTTP请求还在继续... } } // 发送请求: request.open('GET', '/api/categories'); //open方法有三个参数 第一个参数为请求的方法 ,
//第二个为请求的URL地址,第三个参数代表是否使用异步
//第三个参数默认为true 使用异步,若是为同步,浏览器中止响应知道Ajax完成。 request.send(); request.open('POST', '/api/categories'); request.setHeader//POST请求须要设置HEADER senStr= request.send();//POST请求须要在send中写上要发送的参数;GET不须要,由于参数在URL中
跨域访问—javascript的安全策略
javascript在发送请求时,URL域名必须和当前页面一致,一致的意思是:
若是要访问外域(其余网站)的URL呢?
<script src="http://api.126.net/....?callback=refreshPrice"></script>
取决于sina.com是否愿意将你加入Access-Control-Allow-Origin:或者*
上面这种跨域请求,称之为“简单请求”。简单请求包括GET、HEAD和POST(POST的Content-Type类型
仅限application/x-www-form-urlencoded、multipart/form-data和text/plain),而且不能出现任何自定义头(例如,X-Custom: 12345) CSS中的跨域请求
/* CSS */ @font-face { font-family: 'FontAwesome'; src: url('http://cdn.com/fonts/fontawesome.ttf') format('truetype'); /*CDN服务器的Access-Control=Allow-Origin不给权限,则没法加载字体资源*/ }
对于PUT、DELETE以及其余类型如application/json的POST请求,在发送AJAX请求以前,浏览器会先发送一个OPTIONS请求
(称为preflighted请求)到这个URL上,询问目标服务器是否接受:
OPTIONS /path/to/resourceHTTP/1.1 Host: bar.com Origin: http://my.com Access-Control-Request-Method: POST
服务器必须响应并明确指出容许的Method,才会继续发送Ajax,不然抛出错误:
HTTP/1.1 200 OK Access-Control-Allow-Origin: http://my.com Access-Control-Allow-Methods: POST, GET, PUT, OPTIONS Access-Control-Max-Age: 86400
因为以POST、PUT方式传送JSON格式的数据在REST中很常见,因此要跨域正确处理POST和PUT请求,服务器端必须正确响应OPTIONS请求。
在javascript中全部代码都是单线程执行的。 全部javascript中全部的网络操做,浏览器事件都必须异步执行,异步执行可用回调函数实现。
异步函数会在未来的某个时间点出发一个函数调用。 如Ajax
request.onreadystatechange = function () { // 状态发生变化时,函数被回调
if (request.readyState === 4) { // 成功完成// 判断响应结果:
if (request.status === 200) { // 成功,经过responseText拿到响应的文本:
return success(request.responseText); } else { // 失败,根据响应码判断失败缘由:
return fail(request.status);
} } else { // HTTP请求还在继续... } }
假若有以下的写法,先统一执行ajax逻辑,不关心结果如何,
var ajax=ajaxGet("http://...");
而后根据成功、失败调用sucess或者fail函数。
ajax.ifSuccess(success); ajax.ifFail(fail);
Promise的then函数接受一个处理函数,此函数在处理成功时调用;catch接受一个处理函数,此函数在失败时调用。 例如:
function test(resolve, reject) { //若是成功调用resolve resolve('200 OK'); //若是失败,调用reject reject('timeout in ' + timeOut + ' seconds.'); } var p1 = new Promise(test);//test为要执行的函数,接收两个函数做为参数,一个在成功时用,另外一个在失败时
var p2 = p1.then(function (result) { console.log('成功:' + result); }); var p3 = p2.catch(function (reason) { console.log('失败:' + reason); });
var job1 = new Promise(function (resolve, reject) { log('start new Promise...'); resolve(123); }); job1.then(job2).then(job3).catch(handleError);//链式处理
同步
var p1 = new Promise(function (resolve, reject) { setTimeout(resolve, 500, 'P1'); }); var p2 = new Promise(function (resolve, reject) { setTimeout(resolve, 600, 'P2'); }); // 同时执行p1和p2,并在它们都完成后执行then: Promise.all([p1, p2]).then(function (results) { console.log(results); // 得到一个Array: ['P1', 'P2'] });
有一个返回便可收工
var p1 = new Promise(function (resolve, reject) { setTimeout(resolve, 500, 'P1'); }); var p2 = new Promise(function (resolve, reject) { setTimeout(resolve, 600, 'P2'); }); //只须要得到先返回的结果便可,多余是为了容错 Promise.race([p1, p2]).then(function (result) { console.log(result); // 'P1' });
Promise能够把不少异步任务以并行和串行的方式组合起来执行。
没有canvas,绘图只能用flash插件,用javascript与flash进行交互。
<canvas id="test-stock" width="300px" height="200px">
<p>你的浏览器不支持canvas</p>
</canvas>
getContext(‘2d’)方法会让咱们拿到CanvasRendringContext2D对象,全部绘图都要经过这个对象来完成。
3D图像要使用WebGL,getContext(‘Webgl’)
canvas坐标系统
Stroke 勾勒边框 反义词 fill来进行填充
canvas除了能绘制基本形状和文本,还能够实现动画、缩放、各类滤镜、像素变换等高级操做,若是要实现很复杂的操做,从如下几条优化: