js进阶一:对象,函数
对象
内建对象
- 内建对象指由ES标准定义的对象,任何对ES标准的实现都可以使用这些对象,不用新建可直接调用对象
比如:Math String Number Boolean RegExp
Math.Pi;
String(200);
宿主对象
- 宿主对象指由JS运行环境为我们提供的对象,目前对于我们来说就是指浏览器为我们提供的对象
- 比如BOM对象 和 DOM对象
- window document console 。。。。。。
自定义对象,由自己定义的对象
创建对象
var obj = new Object();
var obj = {}
var obj2 = {
name:"孙悟空",
age:18,
address:"花果山",
//对于特殊的属性名,可以通过双引号引起来
"123":"hello",
test:{ name:"猪八戒",
age:28,
address:"高老庄"
}
};
工厂模式创建对象
function createPerson(name , age , gender , address){
//创建一个对象
var obj = new Object();
//向对象中添加属性
obj.name = name;
obj.age = age;
obj.gender = gender;
obj.address = address;
obj.sayName = function(){ console.log("大家好,我是:"+this.name);
};
return obj;
}
对象属性操作
属性值可以是任意的数据类型(String Number Boolean Null Undefined)也可以一个对象
添加/修改属性
对象.属性名 = 属性值 , 易用, 但有时不能用
obj.name = "孙悟空";
obj.age = 18;
obj.gender = "男";
对象["属性名"] = 属性值 , 难写, 但更通用
obj["123abc"] = "你好";
var str = "var";
console.log(obj[str]);
删除属性
delete 对象.属性名
delete obj.name;
查看属性
对象.属性名
var gender = obj.gender;
对象["属性名"]
obj["123abc"]
检查对象中是否含有某个属性
"属性名" in 对象
"name" in obj;
对obj中的属性进行枚举
for(变量 in 对象){}
通过这个方法对获取到未知属性名,并获取到属性值,
返回属性名给n 返回的属性名是String类型
for(var n in obj){
console.log(n + " : " +obj[n]);
}
对象方法
当一个对象的属性是一个函数时,我们称这个函数是这个对象的方法
obj.sayName = function(){
console.log("Hello 大家好 我是孙悟空~~~");
};
代码块
{ }
JS中的代码块仅有分组的作用没有其他的作用,代码块中的内容对于代码块外是可见的
函数
函数也是一个对象,具有Object的所有的功能
- 不同是的函数对象可以保存一些可执行的代码,并且在需要的时候可以调用这些代码
创建
方式一
function 函数名字(形参1,形参2,...形参n){
语句...
}
function sum(a , b ,c){
}
方式二
var 变量 = function(形参1,形参2,...形参n){
语句...
}
var fun2 = function(){
alert("我是又一个函数");
};
函数不写return 默认return undifined
注
-
JS函数在调用时,解析器(浏览器)不会去检查形参的类型和个数
-
也可以传递任意数量的实参
-
如果实参的数量小于形参,则没有对应实参的形参将会使undefined
-
如果实参的数量大于形参,则多余的实参不会使用
-
可以传递任意类型的实参
-
实参可以是任意的数据类型,也可以是一个对象或函数
调用函数
方法一
函数名字(形参1,形参2,...形参n);
方法二
call() / apply()
var obj2 = {
name : "沙和尚",
sayName : function(){
console.log(this.name);
}
};
name = "window中的name";
obj2.sayName.call(window);
-
当调用函数对象的call和apply时都会导致函数立即执行,就相当于调用了函数一样
-
如果通过call和apply去调用一个函数,则call和apply中的第一个参数将会是函数中的this,之后的参数是传入方法中的参数
-
call的参数,需要一个一个列出来
fun2.call(obj,1,2);
-
apply的参数,需要封装为一个数组传
fun2.apply(obj , [1 , 2]);
arguments
-
arguments是一个类数组对象,在它里边保存着函数执行时的实参
-
函数的所有的实参都在arguments中保存,
-
通过arguments即使不定义形参也可以使用获取到传入的实参
function fun(a,b,c){ Array.isArray() 可以用来检查一个对象是否是一个数组 console.log(Array.isArray(arguments)); //获取实参的数量 console.log(arguments.length); //获取指定的实参 console.log(arguments[2]); //callee代表的是当前正在调用的函数对象 console.log(arguments.callee == fun); }
回调函数
你定义的,你没有调用,但最终它执行了: 发生了对应了事件或特定时刻(如点击事件)
自调函数
(function(){
})();
构造函数
构造函数就是普通的函数, 构造函数首字母大写
function Person(name , age){
//通过this,向新对象中添加属性
this.name = name;
this.age = age;
this.sayName = function(){
console.log(this.name);
};
}
创建对象
var dog = new Person();
执行流程
创建一个新的对象
将新创建的对象设置为函数中的this
逐行执行代码
将新建的对象作为返回值返回
prototype原型对象(祖先对象)
-
我们每次创建一个函数浏览器都会为函数添加一个属性叫做prototype,这个属性对应的是一个对象
这个对象就是我们说的原型对象。 -
如果以仅仅以函数的形式去调用函数,则原型对象没有任何作用
-
当以构造函数的形式调用函数时,它所创建的对象中都会有一个隐含的属性
-
我们可以通过"_ proto_"属性来访问这个对象
属性
constructor
注
-
所有的同一类型的对象他们都共享同一个原型对象,这个原型对象就相当于一个公共的区域
-
当我们去调用一个对象的属性或方法时,它会先去对象自身中寻找,如果找到了则直接使用,如果没找到则去原型对象中寻找,如果原型中有则返回原型中的值如果原型中没有,则去原型的原型中寻找,找到了则直接使用依次类推。
-
我们可以将对象中共有的属性或方法统一添加到原型中,这样我们不用添加多个重复的属性或方法,也不会污染全局作用域
注意:Object的原型的原型为null,所以会一直找Object的原型,
如果他里面依然没有,则返回undefined
function MyClass(){
}
//向函数的原型对象中添加一个属性
MyClass.prototype.hello = "你好";
MyClass.prototype.fun = function(){
alert("我是原型中的fun");
};
//创建MyClass的实例
var mc = new MyClass();
var mc2 = new MyClass();
var mc3 = new MyClass();
mc.hello = "mc的你好";
console.log(mc3.__proto__ == MyClass.prototype)
console.log(mc2.hello);
判断方法是否属于对象
per.hasOwnProperty("toString")
显示原型/隐式原型
-
每个函数function都有一个prototype,即显式原型
创建函数的时候自动指向一个空的原型对象 -
每个实例对象都有一个proto,可称为隐式原型
创建函数对象的时候自动添加的,默认值为函数的prototype值 -
对象的隐式原型的值为其对应构造函数的显式原型的值
-
ES6之前不可以直接操作隐士原型的值,可以直接操作显示原型
-
实例对象的隐式原型为构造函数的显示原型的值
原型链(隐式原型链)
查找一个对象的属性时,先在基本属性中查找,如果没有,再沿着proto这条链向上找
原型属性问题
- 读取对象的属性值时, 会自动到原型对象中查找
- 设置对象的属性值时, 不会查找原型链, 如果当前对象中没有此属性,直接添加此属性并设置其值
对象继承
原型链继承
Student.prototype = new Person()
借用构造函数继承(使用call()方法)
function Person(name, age) {
this.name = name
this.age = age
}
function Student(name, age, price) {
Person.call(this, name, age)
this.price = price
}
组合继承(即使用构造函数继承,又使用原型链继承)
this
每次调用函数时,浏览器都会将一个对象作为隐藏的参数传递进函数,这个对象就是函数执行的 上下文对象,我们可以通过this来引用该对象
-
本质上任何函数在执行时都是通过某个对象调用的
-
this就代表调用函数的当前对象
-
在定义函数时, this还没有确定, 只有在执行时才确定
-
当调用函数时没有明确指定当前对象,this就是全局对象window
var obj3 = { name:"沙和尚", age:38, sayName:function(){ function test(){ console.log(this.name); } test(); } }; obj3.sayName();
函数调用方式不同,this的值也不同
-
以方法形式调用,谁调用this就是谁
-
以函数形式调用,this永远是window
-
构造函数的形式调用,this就是新创建的对象
-
通过call和apply调用时,第一个参数会成为this,可以让一个函数在任意对象上调用
-
通过事件绑定中的this就是绑定事件的元素(谁绑定的this就是谁)
作用域
全局作用域
-
所有直接在script标签中编写的代码都在全局作用域中
-
全局作用域在打开网页时创建,在网页关闭时销毁
-
全局作用域中有一个全局对象window,window代表的是浏览器的窗口
-
在全局作用域中创建的变量都会作为window对象的属性保存
-
在全局作用域中创建的函数都会作为window对象的方法保存
-
在全局作用域中创建的变量都是全局变量,可以在页面的任意位置访问
函数作用域
-
函数作用域可以理解为是全局中的小的作用域
-
函数作用域在函数调用时创建,在调用结束时销毁,每调用一次函数就会创建一个新的函数作用域
-
在函数作用域中可以访问到全局变量,而在全局中无法访问到函数作用域中的变量,
在函数中创建的变量如果不写var,则会变成全局变量 -
当我们在函数中使用一个变量时,它会先在自身的作用域中寻找,如果有就直接使用,如果没有则去上一级作用域中寻找,找到则使用,没找到则继续寻找,直到找到全局作用域为止如果全局作用域中依然没有,则报错ReferenceError
-
在函数作用域中也适用变量和函数的声明提前
-
如果在函数作用域中想访问全局变量可以通过window对象来访问
console.log(window.a);
块作用域
ES6的时候才有
作用域链
静态的,在编写代码的时候就确定了变量调用位置
变量声明提前
-
使用var关键字声明的变量,都会在所有的代码执行之前被声明,但是不会赋值
赋值会直到执行到赋值的代码时才执行 -
如果不使用var关键字声明变量,则不会有声明提前的特性
-
如果不写var,直接为变量赋值,则相当于为window对象添加属性
函数声明提前
-
使用函数声明创建的函数,会在所有的代码执行之前创建,
所以我们可以在函数声明前就去调用函数function fun(){ console.log("我是fun函数。。。。"); }
-
使用函数表达式创建的函数,没有声明提前特性,所以不能在它创建之前调用
var name = function fun(){ console.log("我是fun函数。。。。"); }
堆栈
基本数据类型
-
基本数据类型的值是直接保存到栈内存中的
-
基本数据类型的值都是互相独立的,修改一个不会影响到其他的值
-
对基本数据类型相等比较时,比较的是数据的值
引用数据类型
-
引用数据类型对象是保存在堆内存中的,而在变量中保存的是对象的引用(内存地址)
-
我们是通过引用来操作对象的,当修改一个对象时有可能影响到其他的变量
-
两个引用数据类型进行比较时,比较的是对象的地址,如果两个对象一模一样,但是地址不同也会返回false
闭包
- 当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时, 就产生了闭包
闭包到底是什么?
- 使用chrome调试查看
- 理解一: 闭包是嵌套的内部函数(绝大部分人)
- 理解二: 包含被引用变量(函数)的对象(极少数人)
- 注意: 闭包存在于嵌套的内部函数中
闭包的作用
-
使函数内部的变量在函数执行完后, 仍然存活在内存中(其它变量已释放)
-
让函数外部可以读取到函数内部的数据(变量/函数)
闭包的应用
-
回调函数(定时执行函数)
-
模块化
function fun(n,o) { console.log(o) return { fun:function (m) { return fun(m,n) } } } console.log(fun(0).fun(1).fun(2)) // 另一个 function f1(){ var n=999; nAdd=function(){n+=1} function f2(){ alert(n); } return f2; } var result=f1(); result(); // 999 nAdd(); result(); // 1000
-
将所有的数据和功能都封装在一个函数内部(私有的)只向外暴露一个对象(函数), 并给对象添加一些公开的方法
-
模块的使用者, 只需要通过模块暴露的对象调用方法来实现对应的功能
闭包的缺点及解决
1. 缺点
-
函数执行完后, 被引用的变量没有释放, 占用内存时间会变长
-
容易造成内存泄露
2. 解决
-
能不用闭包就不用
-
及时释放