创建Object实例
var person = new Object();
var person = {};
访问对象属性
点表示法 person.age;
方括号表示法 person[“age”] 优点:可以通过变量来访问属性
特点:每一项可以保存不同类型的数据;大小可以动态增长。
创建数组
读取和设置数组的值
使用方括号和基于0的数字索引
length属性
获取数组的项数。不是只读的,可修改。
用于添加新项:colors[colors.length]=“black”;
检测数组
经典问题之确定某个对象是不是数组
对于一个全局作用域而言,value instanceof Array即可,但是对于一个frame以上的网页,不同框架之间原生创建的数组具有不同的构造函数。
新增方法:Array.isArray(value)。
转换方法
使用对象自带的方法:
栈方法
队列方法
重排序方法
操作方法
位置方法
迭代方法-5个
都不会修改数组原来的值。两个参数:函数;作用域对象(影响this的值)。
函数会收到三个参数:数组项的值,该项索引,数组对象本身。
首先对每一项运行函数,然后进行不同的操作。
缩小方法
接受两个参数:函数;作为缩小基础的初始值。
传入的函数会收到四个参数:前一个的返回值,当前值,当前索引值,数组对象本身。
创建日期对象
继承的方法
日期格式化方法
一些专门用于将日期格式化为字符串的方法:
日期/时间组件方法
创建正则表达式
var expression = / pattern / flags;
字面量表示你真正要表达的东西。
RegExp属性
RegExp方法
exec()
接受一个参数,要匹配的字符串。返回包含第一个匹配项信息的数组,还附带两个属性,input和index。
test()
接受一个字符串参数。匹配时返回true,否则返回false。
RegExp构造函数属性
常用正则表达式
^[0-9]*$ 数字
^\d{n}$ n位的数字
^\d{m,n}$ m-n位的数字
^(0|[1-9][ 0-9]*)$ 零和非零开头的数字
^([1-9][0-9]*)+(.[0-9]{1,2})?$ 非零开头的最多戴两位小数的数字
^(\-)?\d+(\.\d{1,2})$ 带1-2位小数的正数或负数
定义函数
没有重载
后面定义的函数会覆盖前面的。本质相当于变量的重新赋值。
作为参数或返回值的函数
作为返回值:可以生成具有某些可动态生成的函数。
函数内部属性
函数属性和方法
Boolean、Number、String类型
当读取一个基本类型值的时候,后台会创建一个对应的基本包装类型的对象从而让我们能够调用一些方法来操作这些数据。
基本包装类型值存在于一行代码的执行瞬间,因此不能在运行时添加属性和方法。
可以显示地调用Boolean、Number、String来创建基本包装类型的对象。typeof会返回Object,转化为布尔值时为true。
在表达式中使用Boolean对象时,返回true,因为是对象。
建议不要使用。
字符方法
字符串方法
位置方法
trim方法
大小写转换方法
模式匹配方法
第二个参数可以包含字符序列
$$ $
$& 匹配整个模式的子字符串
$' 匹配的字符串之前的字符串
$` 匹配的字符串之后的字符串
$n 匹配第n个捕获组的子字符串
$nn 同上
第二个参数也可以是函数。向函数传递三个参数:模式的匹配项,在字符串中的位置,原始字符串。
function(match,pos,originalText){}
例:function htmlEscape(text){
return text.replace(/[<>"&]/g,function(match,pos,originalText){
switch(match){
case "<":
return "<";
case ">":
return ">";
case "&":
return "&";
case "\":
return """;
}
})
}
构造函数的方法
“兜底儿对象”
URI编码方法
eval()方法
Global对象的属性
undefined NaN Infinity Object Array Function Boolean String Number Date RegExp Error EvalError RangeError RefenceError SyntatError TypeError URIError
window对象
属性
min()和max()方法
用于确定一组数值中的最大值和最小值。
舍入方法
生成随机数方法
其他方法
Math.abs(num)
Math.exp(num)
Math.log(num)
Math.pow(num,power)
Math.sqrt(num)
Math.acos(x) 反余弦值
Math.asin(x)
Math.atan(x)
Math.atan2(y,x)
Math.cos(x)
Math.sin(x)
Math.tan(x)
属性类型
js中有两种属性:数据属性和访问器属性。
数据属性
四个特性:configurable(能否删除、修改属性特性,默认true) enumerable(能否用for-in循环遍历,默认true) writable(value是否可改,,默认true) value(值)
var person={};
Object.defineProperty(person,"name",{
writable:false;
value:"Nike";
})
访问器属性
不包含数据值,但是包含一对getter和setter函数。
四个特性:configurable enumerable get set
var book={
_year:2019;
edition:1;
}
Object.defineProperty(book,"year",{
get:function(){
return this._year;
},
set:function(newValue){
if(newValue>2019){
this._year=newValue;
this.edition += newValue - 2019;
}
}
})
book.year=2020;
alert(book.edition) // 2
定义多个属性
var book={};
Object.defineProperties(book,{
year:{
value:2020;
},
edition:{
value:2;
}
})
读取属性的特性
函数根据传入的参数,显式new一个Object,并将参数设置到对象中,最后返回该对象。
问题:无法识别对象类型。
使用自定义的构造函数,在该函数中直接将方法和属性赋值给this对象。
按照惯例,构造函数以大写字母开头。
使用
使用new来调用,与普通函数的区别只在于调用方式。
如果直接调用,this会指向不同的对象。在全局中将指向global对象,在浏览器中则是window对象。
问题
定义方法时,每一个实例都重新定义了同名方法,这完全是没必要的。不同实例上的同名函数是不相等的。
若将函数移到全局,可以减少重复定义,但这破坏了封装性。所以参考↓原型模式。
理解原型对象
区分好这三个概念:
构造函数(Person),含有prototype属性,指向原型对象。
原型对象(Person.prototype),自动获得一个constructor属性,这个属性指向构造函数。
实例(person1. __ proto __),含有一个指针 [ [ prototype ] ] ,浏览器中定义了一个__proto__属性,执行原型对象。
isPrototype()
确定一个对象与一个原型是否有关系
Person.isPrototype(person1)//true
Object.getPrototypeOf()
获取原型对象
Object.getPrototypeOf(person1)==Person.prototype
hasOwnProperty()
检测实例属性。
只有属性存在于实例中,才返回true。
属性的读取顺序
首先在实例内搜索,找不到的话才去原型中寻找。
可以重名,只屏蔽而非重写。
可以使用delete删除实例属性。
原型与in操作符
用于确定是否原型属性的函数
function hasPrototypeProperty(object,name){
return !object.hasOwnProperty(name)&&(name in object);
}
for-in
枚举所有enumerable的属性,包括原型属性和实例属性。
Object.keys()方法
获取所有可枚举属性,接受一个对象作为参数,返回一个包含所有可枚举属性的字符串数组。
Object.getOwnPropertyNames()
获取所有属性(包括不可枚举)
简化原型语法
可以用对象字面量来写原型Person.prototype。
注意此时的constructor属性指向Object,无法确定其类型。可以显式地将属性设置为适当的值,如:constructor:Person;
此时constructor是可以枚举的,所以还应该使用Object.definePropoty()
Object.defineProperty(Person.prototype,"constructor",{
enumerable:false;
value:Person;
})
原型的动态性
一般而言,对原型对象所做的任何修改都能直接从实例上反映出来。
但是如果重写整个原型,会切断构造函数与最初原型之间的联系,因为调用构造函数的时候,会给实例添加一个指向原型的 [ [ prototype] ],重写整个原型就相当于将原型修改为另外一个对象。也就是说,重写原型之前构造的实例,访问不到重写之后的原型。
原生对象的原型
原生的引用类型,也是这样创建的。可以给原生对象的原型,添加属性方法。
String.prototype.startsWith=function(text){
return this.indexOf(text)==0;
}
问题
原型模式最大的问题都是其共享的本性所导致的。
所以一般和构造函数结合着使用↓。
最常见的方式。
function Person(name,age){
this.name=name;
this.age=age;
}
Person.prototype={
constructor:Person;
sayName:function(){
alert(this.name);
}
}
实现封装。“这种方法非常完美。”
动态就是指,根据条件判断的结果,来决定是否向原型中添加方法。
在构造函数内部:
if(typeof this.sayName != "function"){
Person.prototype.sayName=function(){
alert(this.name;)
}
}
if语句控制函数的添加只实现一次。
同样不能用对象字面量重写原型。
思想
创建一个函数,该函数的作用仅仅是封装创建对象的代码,然后再返回新创建的对象。
除了使用new操作符并把使用的包装函数叫做构造函数之外 ,其实和工厂函数是一模一样滴。
使用:new 工厂函数
例:“想创建一个具有额外方法的特殊数组”
function SpecialArray(){
var values=new Array();
values.push.apply(values,arguments);
values.toPipedString = function(){
return this.join(",");
}
return values;
}
没有公共函数,其方法也不引用this的对象
让原型对象等于另一个类型的实例。
构造函数、原型和实例的关系:每个构造函数都有一个原型对象,原型对象包含一个指向构造函数的指针(constructor),而实例都包含了一个指向原型对象的内部指针。
实现原型链
创建父亲实例,并将父亲赋值给儿子的原型而实现的。本质是重写儿子的原型对象,代之以父亲的实例。
原型链中的搜索机制:一环一环地搜索,直到原型链末端才停下来。
默认的原型
Object.prototype 这也是所有自定义类型都会继承toString(),valueOf()等默认方法的根本原因。
确定原型的类型
只要是原型链中出现过的原型,均可以是其类型。
判断方法:instanceof;原型.isPrototypeOf()
谨慎地定义方法
给原型添加方法的代码一定要放在替换原型的语句之后。
不能用对象字面量创建原型方法,否则会切断原型链。
问题
父亲的属性值容易被儿子修改,而修改之后所有的儿子都会受到影响。
实践中很少会直接使用原型链。
也叫伪造对象、经典继承
思想:在子类构造函数的内部调用父类构造函数。
构造父亲的时候传递参数,使得每一个子类可以拥有自己想要的父亲。
实现
在子类构造函数中,调用father.call(this,“argus”);
问题:子类失去了复用性,每一个都需要独自创建。
借用构造函数的方式很少单独使用。
也叫伪经典继承
结合原型链和借用构造函数,融合了它们的优点,最常用。
思路
使用原型链继承原型,使用构造函数继承实例的属性。
也就是把要共享的部分,加到原型链中。而私有的部分,则加到实例中。
属性是通过实例屏蔽原型的方法来实现私有的,也就是说,同名属性既存在于实例中,又存在于原型中。
function father(name){
this.name=name;
}
father.prototype.sayName=function(){
alert(this.name);
}
function son(name,age){
father.call(this,name);
this.age=age;
}
son.prototype=new father();
思想
借助已有的对象创建新对象,也就是少掉了new一个父亲的过程。
称为原型式继承,是因为将儿子的原型直接设有已有的对象以作父亲。
function object(father){
function son(){};
son.prototype=father;
return new son();
}
思路
创建一个仅用于封装继承过程的函数。
function createSon(father){
var son=Object.create(father);
son.sayHi=function(){
alert("hello world!");
}
return son;
}
寄生组合式继承式引用类型最理想的继承范式。
用于解决组合继承中最大的问题:无论在什么情况下,总会调用超过两次的父亲构造函数。
思路
通过借用构造函数继承属性,通过原型链的混成形式来继承方法。
实现
function inherit(son,father){
var prototype=Object.create(father.prototype);
prototype.constructor=son;
son.prototype=prototype;
}
函数声明
function sayName(name){
alert(name);
}
函数表达式
var sayName=function(name){
alert(name);
}
函数声明会提升,而函数表达式不会。
arguments.callee指向调用的函数。于是可以使用匿名的函数声明来执行递归。
概念
闭包:“怀孕的妈妈”,是指有权访问另一个函数作用域中的变量的函数。
销毁时间
闭包函数在执行完毕之后,其活动对象也不会销毁,因为其中的匿名函数的作用域链仍在引用这个活动对象。等到匿名函数被销毁后,闭包的活动对象才会被销毁。
闭包与变量
闭包只能取得包含函数中任何变量的最后一个值。
解决方法,创建另一个匿名函数。
function createFunc(){
var res= new Array();
for(var i=0;i<10;i++){
res[i]=function(num){
return function(){
return num;
}
}
}
return res;
}
关于this对象
匿名函数的执行环境具有全局性,因此其this对象通常指向window。
让匿名函数访问外部函数中的变量
方法:在外部创建一个新的变量在指向当前对象。
var name="the window";
var object={
name:"my object";
getFunc:function(){
var that=this;
return function(){
return that.name;
}
}
}
alert(object.getFunc()()); // "my object"
*内存泄漏
没有块级作用域(ES5中),意味着块语句中的变量,实际上是在包含函数中创建的,而不是在语句中创建的。
匿名函数可以用于模仿块级作用域。
任何函数中定义的变量,都可以认为是私有变量。
特权方法
有权访问私有变量和私有函数的公有方法。
构造函数模式
funciton MyObject(){
var priVar=2020;
function priFuc(){
alert("hello");
}
this.publicMethod=function(){
priVar++;
return priFunc();
}
}
使用的时候需要new一个MyObject,每一个新对象都拥有自己的变量和方法。
缺点就是每个实例都需要创建同样的一组方法。
原型模式
生成静态私有变量
(funciton(){
var priVar=2020;
function priFuc(){
alert("hello");
}
MyObject=function(){};
Myobject.prototype.publicMethod(){
priVar++;
return priFunc();
}
})();
初始化未经声明的变量,总是会创建一个全局变量,所以能在外部访问到。
创建静态私有变量会因为使用原型而增进代码复用,但每个实例都没有自己的私有变量。
模块模式(module pattern)
单例,是指只有一个实例的对象。
使用一个匿名函数,返回值为一个字面量对象,对象中只包含可以公开的方法和属性。
使用的情况:如果必须创建一个对象,并以某些数据对其进行初始化,同时还要公开一些能够访问这些私有数据的方法,那就可以使用模块模式。
为单例添加私有变量和特权方法:
var singleton = function(){
var priVar=2020;
function priFunc(){
alert("hello");
}
return {
publicPro:true;
publicMethod:function(){
priVar++;
return priFunc();
}
}
}();
增强的模块模式
适用于单例必须是某种类型的实例的。
实现:
将对象字面量编程var obj=new xxx; 添加属性和方法;return obj;即可。