文章目录
创建函数对象的方式 & 函数vs方法
1. 使用构造函数的方式(不使用)
var fun = new Function("console.log('Hello')") //创建一个函数对象(可以将要封装的代码以字符串的形式传递给构造函数)
2. 使用函数声明来创建一个函数
function 函数名([形参1,形参2,...形参n]){
console.log("Hello")
}
3. 使用函数表达式来创建一个函数(匿名函数赋值给一个变量)
var 函数名 = function([形参1,形参2,...形参n]){
console.log("Hello")
}
注意:
对象的属性值可以是任何的数据类型,也可以是一个函数
obj.sayName = function(){
.....
} (写法1)
写法2:
var obj = {
name:"猪八戒",
age:18,
sayName:function(){
....
}
};
obj.sayName();
如果一个函数作为一个对象的属性保存,则
称这个函数是这个对象的方法
调用这个函数 -> 调用对象的方法(method)
但它只是名称上的差异,本质上无区别
立即执行函数
1. 引入:
function(){
alert("我是一个匿名函数");
}
如果单纯这么写,则会报错,因为js会把{...}当成一个代码块,然后不认识前面的function()
可以给这个函数加上一个括号(),表示这整个匿名函数是个整体
(function(){
alert("我是一个匿名对象");
})
如何调用这个匿名函数呢,调用函数一般格式是 函数对象()
这里上面这种即为函数对象,所以调用形式为:
(function(){
alert("我是一个匿名对象");
})();
该种函数称为“立即执行函数”
2. 格式:
1) 无参
(function(){
alert("我是一个匿名对象");
})();
2) 有参
(function(a, b){
console.log("a = " + a);
console.log("b = " + b);
})(123,234);
3. 特点:
4) 函数定义完,立即被调用
5) 往往只会执行一次
call() 和 apply()
- 这两个方法都是函数对象的方法,需要通过函数对象去调用
> 函数对象 vs 函数
> 如: function fun(){
> alert("xxxx");
> }
> fun(); //函数
> fun //函数对象
- 当对函数对象调用call() 和 apply() 方法,都会执行函数
如: fun.call()
fun.apply()
fun();
- 当调用call和apply() 可以将一个对象指定为第一个参数
此时,这个对象将会称为函数执行时的this
如: var obj = {};
function fun() {
alert(this);
}
fun.call(obj); //Object
fun.apply(obj); //Object
fun(); //Window
- call()方法可以将实参在对象之后一次传递
function fun2(a,b){
alert(a);
alert(b);
}
fun2.call(obj); //undefined undefined
fun2.call(obj,2,3); //2 3
- apply()方法需要将实参封装到一个数组中统一传递
fun2.apply(obj,[2,3]); //2 3
this
function fun(){ //隐藏了一个名为this的形参
console.log(this);
}
var obj = {
sayName:fun;
}
解析器在调用函数时,(浏览器)每次都会向函数内部传递一个隐含的参数this(this指向的是一个对象,这个对象称为“函数执行的上下文对象”)
根据函数的调用方式的不同,this会指向不同的对象
1)以函数的形式调用时,this永远都是window
fun(); //window
2)以方法的形式调用时,this就是调用方法的那个对象
obj.sayName(); //object,obj
3)以构造函数的形式调用时,this就是新创建的那个对象
new Dog();
4) 使用call和apply调用时,this是指定的那个对象
arguments
在调用函数时,浏览器每次都会传递进两个隐含的参数:
1. 函数的上下文对象 this
2. 封装实参的对象 arguments
- arguments:一个类数组对象,它也可以通过索引来操作数据,也可以获取长度
function fun() {
//console.log(arguments instanceof Array); //false
//console.log(Array.isArray(arguments)); //false
console.log(arguments.length);
}
- 在调用函数时,我们所传递的实参都会在arguments中保存
- arguments.length可以用来获取实参的长度
fun("hello"); //1
fun("hello",true); //2
function fun() {
console.log(arguments[0]);
}
fun("hello",true); //hello
- 即使不定义形参,也可以通过arguments来使用实参
只不过比较麻烦(定义了实参,也可以)
arguments[0] 表示第一个实参
arguments[1] 表示第二个实参
- 它里面有一个属性 callee,对应一个函数对象,就是当前正在执行的函数对象
function fun(a,b) {
console.log(arguments.callee);
}
fun("hello", true); //function fun(a,b) {.....}
使用工厂方法创建对象——大批量创建对象
function createPerson(name,age,gender) {
var obj = new Object();
//向对象添加属性
obj.name = name;
obj.age= age;
obj.gender= gender;
//返回新的对象
return obj;
}
var obj2 = createPerson("xx",28,"男");
function createDog(name,age,gender){
var obj = new Object(); //使用的构造函数都是Object,创建的对象都是Object这个类型,无法具体区分出多种不同类型的对象
... ...
}
为了解决上述的问题,引入构造函数
使用构造函数创建对象
创建一个构造函数,专门用来创建Person对象的
构造函数 vs 普通函数
相同点:构造函数就是一个普通的函数,创建方式和普通函数没有区别
区别:
1) 构造函数习惯上首字母大写
2) 调用方式不同:
普通函数直接调用
构造函数:使用new关键字来调用
构造函数的执行流程:
1)立刻创建一个新的对象
2)将新建的对象设置为函数中的this,在构造函数中可以使用this来引用新建的对象
3)逐行执行函数内的代码
4)将新建的对象作为返回值返回
使用同一个构造函数创建的对象,称为一类对象,也将一个构造函数称为一个类。
将通过同一个构造函数创建的对象,称为是该类的实例。
function Person(){
this.name = name;
this.age = age;
this.sex = sex;
this.sayName = function(){
alert(this.name);
}
}
function Dog(){
}
var per = new Person("xxx",18,"male");
var per2 = new Person("xxx2",19,"female");
var dog = new Dog();
//使用instanceof可以检查一个对象是否是一个类的实例
console.log(per instanceof Person); //true
console.log(dog instanceof Person); //false
//所有的对象都是Object的后代
console.log(dog instanceof Object); //true
console.log(per instanceof Object); //true
问题 & 解决方式:原型
function Person(name, age, gender){
this.name = name;
this.age = age;
this.gender = gender;
//向对象中添加一个方法
this.sayName = function(){
alert("Hello,I am " + this.name);
}
}
引入问题:sayName方法定义在构造方法中,每次创建一个对象(执行一次构造函数),就会创建一个新的sayName方法(内存中开辟一个空间,存放这个方法),则:所有实例的sayName都是唯一的,且方法都是一样的,浪费空间,完全可以用让所有的实例对象共享同一个方法
解决方式1:将sayName方法定义在全局作用域中
function fun() {
alert("Hello,I am " + this.name);
};
function Person(name, age, gender){
this.name = name;
this.age = age;
this.gender = gender;
//向对象中添加一个方法
this.sayName = fun;
}
局限(方式1):
1) 将函数定义在全局作用域中,污染了全局作用域的命名空间(名字不可重复)
2) 定义在全局作用域中很不安全(开发中往往是团队开发,很可能重名覆盖)
解决方式2(改进方式1):原型prototype
我们所创建的每一个函数,解析器(浏览器)都会向函数中添加一个属性prototype(这是属性对应一个对象-原型对象)
每个函数的prototype都是唯一的
如果函数作为普通函数,调用prototype没有任何作用
当函数以构造函数的形式调用时(new Dog()),它所创建的对象(实例对象)中都会有一个隐含的属性 指向该构造函数的原型对象,我们可以使用__proto__来访问该属性
原型对象就相当于一个公共的取域,同一个类的所有实例都可以访问到这个原型对象
我们可以将对象中共有的内容,统一设置到原型对象中
当我们访问对象的一个属性或方法时,会现在对象自身中寻找,如果有则直接使用;
若没有,则到原型对象中寻找
创建构造函数时,可以将这些对象共有的属性和方法,统一添加到构造函数的原型对象中,
这样不用分别为每一个对象添加,也不会影响到全局作用域,就可以使每个独享都具有这些属性和方法了
function MyClass(){
}
//向MyClass的原型中添加属性a
MyClass.prototype.a = 123;
//向Person的原型中添加一个方法
Person.prototype.sayHello = function(){
alert("Hello,I am " + this.name);
}
var mc = new MyClass();
var mc2 = new MyClass();
console.log(MyClass.prototype);
console.log(mc2.__proto__ == MyClass.prototype); //true
//向mc中添加a属性
mc.a = "我是mc中的a";
console.log(mc.a); //我是mc中的a
console.log(mc2.a); //123
//使用in检查对象中是否含有某个属性时,如果对象中没有,但是原型中有,也会返回true
console.log("a" in mc2); //true
//对象的hasOwnProperty()检查对象自身中是否含有该属性
console.log(mc2.hasOwnProperty("a")); //false
console.log(mc2.hasOwnProperty("hasOwnProperty")); //false
console.log(mc2.__proto__.hasOwnProperty("hasOwnProperty")); //false
//mc2的原型对象也没有,原型对象是对象,所以它也有原型,则也有__proto__属性
/* 当我们使用一个对象的属性或方法时,
1) 自身中有,则直接使用
2) 如果自身没有,则去原型对象中寻找,如果原型对象中有,则使用
3) 如果原型对象中也没有,则去原型的原型中寻找,直到找到Object对象,则停止(如果在Object中依然没有找到,则返回undefined)
*/
console.log(mc2.__proto__.__proto__);
console.log(mc2.__proto__.__proto__.hasOwnProperty("hasOwnProperty")); //true
console.log(mc2.__proto__.__proto__.__proto__); //null
垃圾回收(GC)
- 就像人生活的时间长了会产生垃圾一样,程序在运行过程中也会产生垃圾
这些垃圾积攒过多以后,会导致程序运行的速度过慢
- 当一个对象没有任何的变量或属性对它进行引用,此时我们将永远无法操做该对象
此时,这种对象就是一个垃圾,这种对象过多占用大量的内存空间,导致程序运行变慢,
所以这种垃圾必须进行清理
var obj = new Object();
//对对象进行各种操作
... ...
obj = null;
- 在JS中拥有自动的垃圾回收机制,会自动将这些垃圾对象从内存中销毁,
我们不需要也不能进行垃圾回收的操作
- 需要做的只是将不再使用的对象设置null即可
字面量 vs 变量
1. 字面量:一些不可改变的值
1)如:1 2 3 4 5 alert(12323232323)中的12323232323
2)字面量都是可以直接使用,但是我们一般都不会直接使用字面量
2. 变量:变量可以用来保存字面量,而且变量的值是可以任意改变的
变量更加方便使用,所以在开发中都是使用变量去保存一个字面量
而很少直接使用字面量
1) 声明:在js中使用var关键字来声明一个变量,如:
var a; //声明变量
a = 123; //为变量赋值
a = 456;
3. 数据类型指的就是字面量的类型
在JS中,一共有六种数据类型
基本数据类型:
String 字符串
Number 数值
Boolean 布尔值
Null 空值
Undefined 未定义
引用数据类型:
Object对象
Null 和 Undefined
1.Null(空值) 类型的值只有一个,就是null
1) null这个值专门用来表示一个为空的对象
2) 使用 typeof 检查一个null值时,会返回object
例: var a = null;
console.log(typeof a); //object
2.Undefined(未定义)类型的值只有一个,就是undefined
1) 当声明一个变量,但并不给变量赋值时,它的值就是undefined
var b;
console.log(b); //undefined
2) 使用typeof 检查一个undefined值时,也会返回undefined
例: console.log(typeof b);