版本三开始看一边
1基础
JavaScript存在6种数据类型
number,string,undefined,boolean,null,object
强制转换方法
Number()
Boolean()
String()
type of 操作符用来检测数据类型(只能用来精确的检测基本数据类型)
console.log(typeof 1); //number console.log(typeof false);//boolean console.log(typeof undefined);//undefined console.log(typeof 'string');//string console.log(typeof Function);//function console.log(typeof null);//object console.log(typeof {a:1});//object
instanceof 操作符用来检测引用类型的类型()
例如 A instanceof B ; 检测A是否是B的实例
var arr=[1,2,3] var obj={a:1} var fun=()=>{} console.log(arr instanceof Array);//true console.log(obj instanceof Array);//false console.log(fun instanceof Array);//false console.log(arr instanceof Function);//false console.log(obj instanceof Function);//false console.log(fun instanceof Function);//true //JS中 所有的引用类型都是Object的实例(万物皆对象) console.log(arr instanceof Object);//true console.log(fun instanceof Object);//true console.log(obj instanceof Object);//true
Number
- 分为 number和NaN
- 精度又分为整型和浮点型(进度最高是17位小数点)
- 整型方法 parseInt()
- 浮点型方法 parseFloat()
- toFixed(num) 返回数值的字符串会保留num个小数位表示
- toString(num) 将数字转换成几进制
- toLocalString() 返回数值的字符串会保留三位小数
- valueOf() 返回原来的数值
-
var num=10.23848;console.log(num.toLocaleString());//返回对象表示的基本类型的数值保留三位小数(四舍五入)console.log(num.toFixed(2));//10.24 保留几位小数会四舍五入console.log(num.toString(8));//重写了toString(num)方法 将数字转换成几进制console.log(num.valueOf());//返回对象表示的基本类型的数值
3.number能取得的最大值和最小值
- Number.MAX_VALUE
- Number.MIN_VALUE
isNaN()判断一个类型的值是不是NaN
console.log(isNaN(10)) console.log(isNaN('10')) console.log(isNaN(true)) console.log('-------------') console.log(isNaN('10a')) console.log(isNaN('blue')) console.log(isNaN(NaN)) console.log(isNaN('a10'))
各类型转换成Boolean为false的情况
//数字转换成Boolean为false的情况 console.log(Boolean(0)) console.log(Boolean(NaN)) //字符串转换成Boolean为false的情况 console.log(Boolean('')) //null转成Boolean为false console.log(Boolean(null)) //undefined转成Boolean为false console.log(Boolean(undefined))
Object类型(object的每个实例都具有下列属性和方法)
var obj=new Object();
constructor :保存着用于创建当前的函数的对象;构造函数(constructor)就是Object();
hasOwnProperty(propertyName) :判断给定的属性在当前对象实例中是否存在(不是原型种)[propertypName必须用字符串形式包裹起来]
例如: obj.hasOwnProperty('name');
isPrototypeOf(object) 用来判断传入的对象是否是传入对象的原型
propertyIsEnumerable(propertyName):用于检测给定的属性是否能够使用for-in语句进行枚举[propertypName必须用字符串形式包裹起来]
toLocaleString():返回对象的字符串表示.该字符串与执行环境的地区对应
toString():返回对象的字符串表示
valueOf():返回对象的字符串,数值或布尔值表示,通常与toString()的返回值相同
执行环境和作用域
执行环境:定义了变量或函数有权访问其他数据,决定了它们各自的行为;
全局作用域:当代码在一个环境中执行时,会创建变量对象的一个作用域链(保证对执行环境有权访问的所有变量和函数的有序访问)
局部作用域:局部作用域中的变量可以在局部环境与全局变量互换使用
内部环境可以通过作用域链访问所有的外部环境,但外部环境不能访问内部环境中的任何变量和函数.
延长作用域链的方法两种(这么说是因为有些语句可以在作用域链前端临时增加一个变量对象,该变量对象会在代码执行中被移除)
- try-catch语句中的catch块
- with语句
function buildUrl(){ var qs="?debug=true"; with(location){ var url=href+qs; } return url; } console.log(buildUrl())
函数的内部属性
arguments (是个类数组对象 主要用途是保存函数的参数,)和this(代表的是执行环境)
和callee 和caller(严格模式下不能使用 也不能为caller赋值 否则会导致错误)
这个对象还有一个名叫callee的属性(指向拥有arguments对象的函数)
View Codefunction inner(n){ if(n<=1){ return 1; } return n*arguments.callee(n-1) } // 重写后的inner()函数体内没有在引用函数名inner ,这样无论引用函数时使用的是什么名字,都可以保证完成递归调用 var outer=inner; inner=function(){ return 0; } console.log(outer(5))//120 console.log(inner(5))//0这个对象还有一个名叫caller的属性(保存着调用当前函数的函数的引用,如果在全局作用域中调用当前函数,它的值为null)
View Codefunction outer(){ inner() } function inner(){ // console.log(inner.caller) console.log(arguments.callee.caller) } console.log(inner());//null console.log(outer());//outer
函数属性和方法
length 表示函数接受的命名参数的个数
prototype 是函数的原型函数(prototype属性是不可以枚举的,因此使用for-in无法发现)
每个函数都包含两个非继承来的方法(都是用来改变this的指向 只是接受的参数不同):apply(this,arr) 和call(this,1,3)
apply(this,arr) 接受数组,类数组
View Codefunction sum(num1,num2){ return num1+num2 } function callSum1(num1,num2){ return sum.apply(this,arguments) } function callSum2(num1,num2){ return sum.apply(this,[num1,num2]) }call(this,1,2,3) 接受的参数必须逐个列举出来
View Codefunction sum(num1,num2){ return num1+num2 } function callSum(num1,num2){ return sum.call(this,num1,num2) }
- [[Configurable]]:表示能否通过 delete 删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性。像前面例子中那样直接在对象上定义的属性,它们的这个特性默认值为 true。
- [[Enumerable]]:表示能否通过 for-in 循环返回属性。像前面例子中那样直接在对象上定义的属性,它们的这个特性默认值为 true。
- [[Writable]]:表示能否修改属性的值。像前面例子中那样直接在对象上定义的属性,它们的这个特性默认值为 true。
- [[Value]]:包含这个属性的数据值。读取属性值的时候,从这个位置读;写入属性值的时候,把新值保存在这个位置。这个特性的默认值为 undefined。
View Codevar person = { name: "Nicholas" }; 对于像前面例子中那样直接在对象上定义的属性,它们的[[Configurable]]、[[Enumerable]] 和[[Writable]]特性都被设置为 true,而[[Value]]特性被设置为指定的值。
- [[Configurable]]:表示能否通过 delete 删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为数据属性。对于直接在对象上定义的属性,这个特性的默认值true。
- [[Enumerable]]:表示能否通过 for-in 循环返回属性。对于直接在对象上定义的属性,这个特性的默认值为 true。
- [[Get]]:在读取属性时调用的函数。默认值为 undefined。
- [[Set]]:在写入属性时调用的函数。默认值为 undefined。
(支持 ECMAScript 5 的这个方法的浏览器有 IE9+(IE8 只是部分实现)、Firefox 4+、Safari 5+、Opera12+ 和 Chrome )访问器属性不包含数据值;它们包含一对儿 getter 和 setter 函数(不过,这两个函数都不是必需的)。在读取访问器属性时,会调用 getter 函数,这个函数负责返回有效的值;在写入访问器属性时,会调用setter 函数并传入新值,这个函数负责决定如何处理数据。请看下面的例子。在这个方法之前,要创建访问器属性,一般都使用两个非标准的方法:(没有这个方法之前使用的)__defineGetter__()和__defineSetter__()。(这两个方法最初是由 Firefox 引入的,后来 Safari 3、Chrome 1 和 Opera 9.5 也给出了相同的实现。使用这两个遗留的方法,可以像下面这样重写前面的例子。)
View Code//定义访问器的旧有方法 // var book={ // _year:2004, // edition:1 // } // book.__defineGetter__('year',function(){ // return this._year; // }) // book.__deifneSetter__('year',function(newValue){ // if(newValue>2004){ // this._year=newValue; // this.edition+=newValue-2004; // } // }) // book.year=2005; // console.log(book.edition) //修改单个特性Object.defineProperty(obj,attr,{}) // var book = { // _year: 2004, // edition: 1 // }; // Object.defineProperty(book, "year", { // get: function(){ // return this._year; // }, // set: function(newValue){ // if (newValue > 2004) { // this._year = newValue; // this.edition += newValue - 2004; // } // } // }); // book.year = 2005; // alert(book.edition); //修改多个特性Object.defineProperties(obj,{}) var book={}; Object.defineProperties(book,{ _year:{ value:2014 }, edition:{ value:1 }, year:{ get:function(){ return this._year }, set:function(newValue){ if(newValue>2004){ this._year=newValue; this.edition+=newValue-2004 } } } }) console.log(book.year) console.log(book.edition) console.log(book.year=2050)
读取属性的特性 Object.getOwnPropertyDescriptor()
支持这个方法的浏览器有 IE9+、Firefox 4+、Safari 5+、Opera 12+和 Chrome。
- 使用 ECMAScript 5 的 Object.getOwnPropertyDescriptor()方法,可以取得给定属性的描述符。
- 这个方法接收两个参数:属性所在的对象和要读取其描述符的属性名称。
- 返回值是一个对象,如果是访问器属性,这个对象的属性有 configurable、enumerable、get 和 set;
- 如果是数据属性,这个对象的属性有 configurable、enumerable、writable 和 value。例如:
View Codevar book={}; Object.defineProperties(book,{ _year:{ value:2014 }, edition:{ value:1 }, year:{ get:function(){ return this._year }, set:function(newValue){ if(newValue>2004){ this._year=newValue; this.edition+=newValue-2004 } } } }) var descriptor=Object.getOwnPropertyDescriptor(book,"_year") console.log(descriptor)//{value: 2014, writable: false, enumerable: false, configurable: false} console.log(descriptor.value);//2014 console.log(descriptor.configurable)//false
创建对象
Object 构造函数或对象字面量都可以用来创建单个对象,
缺点:使用同一个接口创建很多对象,会产生大量的重复代码。为解决这个问题,人们开始使用工厂模式的一种变体。
缺点:函数 createPerson()能够根据接受的参数来构建一个包含所有必要信息的 Person 对象。可以无数次地调用这个函数,而每次它都会返回一个包含三个属性一个方法的对象。工厂模式虽然解决了创建多个相似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)。如下面的例子所示View Codefunction createPerson(name,age,job){ var obj=new Object(); obj.name=name; obj.age=age; obj.job=job; // obj.sayName=function(){ // console.log(this.name); // } return obj; } //将公用方法提到外面 添加到原型上 减少内存开销 createPerson.prototype.sayName=function(){ console.log(this.name); } var person1=createPerson('panghu',18,'teacher') var person2=createPerson('xiaofu',18,'doctor') console.log(person1,person2); // console.log(person1.sayName===person2.sayName);//false console.log(person1.sayName===person2.sayName);//true
构造函数模式
- ECMAScript 中的构造函数可用来创建特定类型的对象。
- 像 Object 和 Array 这样创建对象 的原生构造函数,在运行时会自动出现在执行环境中。此外,也可以创建自定义的构造函数,从而定义自定义对象类型的属性和方法。
可以使用构造函数模式将前面的例子重写如下。View Codefunction Person(name,age,job){ this.name=name; this.age=age; this.job=job; this.sayNam=function(){ console.log(this.name); } } var person1 = new Person("Nicholas", 29, "Software Engineer"); var person2 = new Person("Greg", 27, "Doctor");我们注意到,Person()中的代码除了与 createPerson()中相同的部分外,还存在以下不同之处:
- 没有显式地创建对象;
- 直接将属性和方法赋给了 this 对象;
- 没有 return 语句。
此外,还应该注意到函数名 Person 使用的是大写字母 P。按照惯例,构造函数始终都应该以一个大写字母开头,而非构造函数则应该以一个小写字母开头。这个做法借鉴自其他 语言,主要是为了区别于 ECMAScript 中的其他函数;因为构造函数本身也是函数,只不过可以用来创建对象而已。要创建 Person 的新实例,必须使用 new 操作符。(以这种方式调用构造函数实际上会经历以下 4个步骤:)
- (1) 创建一个新对象;
- (2) 将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象);
- (3) 执行构造函数中的代码(为这个新对象添加属性);
- (4) 返回新对象。
我们创建的每个函数都有一个 prototype(原型)属性,这个属性是一个指针,指向一个对象,(而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。)优点:使用原型对象的好处是可以让所有对象实例共享它所包含的属性和方法。在默认情况下,所有原型对象都会自动获得一个 constructor(构造函数)属性,这个属性包含一个指向 prototype 属性所在函数的指针。就拿前面的例子来说,Person.prototype. constructor 指向 Person。而通过这个构造函数,我们还可继续为原型对象添加其他属性和方法。
当为对象实例添加一个属性时,这个属性就会屏蔽原型对象中保存的同名属性;换句话说,添加这个属性只会阻止我们访问原型中的那个属性,但不会修改那个属性。即使将这个属性设置为 null,也只会在实例中设置这个属性,而不会恢复其指向原型的连接。不过,使用 delete 操作符则可以完全删除实例属性,从而让我们能够重新访问原型中的属性
View Codefunction Person(){}; Person.prototype.name='hanmeimei'; Person.prototype.age=20; Person.prototype.job='Software Engineer'; Person.prototype.sayName=function(){ console.log(this.name) }; var person1=new Person(); var person2=new Person(); //obj.hasOwnProperty() 查看obj本身是否拥有某个属性 console.log(person1.hasOwnProperty("name"));//false // name in obj 查看某个属性是否在obj原型上in 操作符会在通过对象能够访问给定属性时返回 true,无论该属性存在于实例中还是原型中 console.log("name" in person1);//true person1.name='Greg'; console.log(person1.name);//hanmeimei 来自实例 console.log(person1.hasOwnProperty("name"));//true console.log("name" in person1);//true console.log('--------') console.log(person2.name);// hanmeimei来自原型 console.log(person1.hasOwnProperty("name"));//true console.log("name" in person2);//true delete person1.name; console.log(person1.name);//hanmeimei 来自原型 console.log(person1.hasOwnProperty('name'));//false console.log("name" in person1);//true在以上代码执行的整个过程中,name 属性要么是直接在对象上访问到的,要么是通过原型访问到的。因此,调用"name" in person1 始终都返回 true,无论该属性存在于实例中还是存在于原型中。同时使用 hasOwnProperty()方法和 in 操作符,就可以确定该属性到底是存在于对象中,还是存在于原型中,如下所示。View Codefunction hasPrototypeProperty(object, name) { if(name in object){ return !object.hasOwnProperty(name); } return alert(name+'不存在实例和原型上') }Object.keys(obj) 要取得对象上所有可枚举的实例属性,可以使用 ECMAScript 5 的 Object.keys()方法。这个方法
接收一个对象作为参数,返回一个包含所有可枚举属性的字符串数组。View Codevar obj = { a: 1, b: 2, c: 3 } obj.__proto__.d = 3; let arr = Object.keys(obj); console.log(arr);//["a", "b", "c"]
为了从视觉上更好地封装原型的功能,更常见的做法是用一个包含所有属性和方法的对象字面量来重写整个原型对象,如下面的例子所示。View Codefunction Person(){ } Person.prototype = { name : "Nicholas", age : 29, job: "Software Engineer", sayName : function () { alert(this.name); } };在上面的代码中,我们将 Person.prototype 设置为等于一个以对象字面量形式创建的新对象。最终结果相同,但有一个例外:constructor 属性不再指向 Person 了。前面曾经介绍过,每创建一个函数,就会同时创建它的 prototype 对象,这个对象也会自动获得 constructor 属性。而我们在这里使用的语法,本质上完全重写了默认的 prototype 对象,因此 constructor 属性也就变成了新对象的 constructor 属性(指向 Object 构造函数),不再指向 Person 函数。此时,尽管 instanceof操作符还能返回正确的结果,但通过 constructor 已经无法确定对象的类型了,如下所示。