1、函数定义方式
function add1(num1,num2){
return num1 + num2;
}
var add2 = function(num1,num2){
return num1 + num2;
}
var add3 = new Function("num1","num2","return num1 + num2");
window.console.log("add1 = " + add1(10,20));
window.console.log("add2 = " + add2(100,200));
window.console.log("add3 = " + add3(1000,2000));
2、对象
var obj = new Object();
obj.name = "张三丰";
alert(obj.name)
var obj = Object();
obj.name="zhangsanfeng";
alert(obj.name);
var obj = {};
obj.name = "zhangjunbao";
alert(obj.name);
var obj = {
name:"张三丰",
age:28,
add:function(num1,num2){
return num1 + num2;
}
};
alert(obj.name);
alert(obj['name']);// 以数组的方式来访问属性,必须有单引号或是双引号
alert(obj.add(10,20));
3、变量作用域和内存
在JavaScript中,基本数据类型(undefined,null,boolean,number,string)是存储在栈内存,其占据的空间是固定的;而引用类型(Object)是存储在堆内存,其占据的空间是要动态分配的,在栈内存中会保留有引用类型的一个地址,当访问这个引用类型的时候,先到栈空间找到该引用类型的地址,然后通过这个地址找到值;
基本数据类型是按值来访问的,引用数据类型是按引用访问的;JavaScript中数据都是按值来传递的。
4、面向对象与原型
在第二节中列举了几种创建对象的方法,那些方法有个问题,就是如果有多个对象的情况下要进行重复实例化,如何解决这个问题,有一下几种方式:
工厂方法创建对象
function createObject(name,age){
var obj = new Object();
obj.name = name;
obj.age = age;
obj.description = function(){
window.console.log(this.name + "," + this.age + ",来自中国......");
}
return obj;
}
var obj1 = createObject("Jackson",29);
var obj2 = createObject("黄飞鸿",129);
obj1.description();
obj2.description();
工厂方法创建对象解决了重复实例化的问题,但是又带来了新的问题:无法区分出到底是哪个对象的实例。
构造函数方式
/**
* 1、所有的构造函数其实就是Object;
* 2、构造函数没有new Object,但在后台会自动new Object();
* 3、this相当于var obj = new Object()中的obj;
* 4、构造函数不需要返回值
* --------------------构造函数规范-----------------------------
* 1、构造函数也是函数,但必须首字母大写;
* 2、必须new 构造函数创建对象
*/
function UserInfo(name,age){
this.name = name;
this.age = age;
this.description = function(){
window.console.log(this.name + "," + this.age + ",来自中国......");
};
}
var user1 = new UserInfo("Hello",29);
var user2 = new UserInfo("word",20);
user1.description();
user2.description();
//--------------------------------------------------------
// 寄生构造函数 = 工厂模式 + 构造函数
// 稳妥构造函数:在构造函数中不能使用this,创建对象的时候不能使用new
function User(name,age){
var obj = new Object();
obj.name = name;
obj.age = age;
obj.description = function(){
window.console.log(name + "," + age + ",来自中国......");
}
return obj;
}
var obj1 = User("Jackson",29);
obj1.description();
对象冒充
function UserInfo(name,age){
this.name = name;
this.age = age;
this.description = function(){
window.console.log(this.name + "," + this.age + ",来自中国......");
};
}
var userReplace = new Object();
UserInfo.call(userReplace,"haha",39);
userReplace.description();
对象原型
上述创建对象的方式,解决了诸如重复实例化的问题、无法区分出到底是哪个对象的问题。但是,还是存在一个问题:对象中的方法占用内存问题!对于每个对象来说,其属性值是可变的,但是其方法基本是不变的,如果采用上述方式创建了对象的实例,那么每个实例中的每个方法都独自占据一定的内存,这样就浪费了大量的内存了,这个时候就可以使用对象原型了。在JavaScript中,每个对象都有原型对象,对象都继承原型对象的所有属性,基于这个原理,对于对象中的方法和常量,就可以使用原型来定义了。
function UserInfo(name,age){
this.name = name;
this.age = age;
}
UserInfo.prototype.description = function(){
window.console.log(this.name + "," + this.age + ",来自中国......");
}
var user1 = new UserInfo("张三丰",200);
user1.description();
function UserInfo(name,age){
this.name = name;
this.age = age;
if(typeof this.description != 'function') {
window.console.log("开始。。。。。。。。。");
UserInfo.prototype.description = function(){
window.console.log(this.name + "," + this.age + ",来自中国......");
}
window.console.log("结束。。。。。。。。。");
}
}
var user1 = new UserInfo("张三丰",200);
user1.description();
var user2 = new UserInfo("张君宝",200);
user2.description();
继承
JavaScript中,所有的对象都有一个原型对象,所有的对象都继承原型对象的属性。
** 1)原型链继承 **
/////////////////原型链继承
function Human(){
this.name = "张山峰";
}
function Man(){
this.sex = "男";
}
// Man所有的对象都继承其原型对象,而Man的原型对象被指定为Human,这样就实现了继承了。
Man.prototype = new Human();
var man = new Man();
window.console.log(man.name + "," + man.sex);
这种继承有一个问题:无法传参给超类,方法不能共享(原型会被打断)。
** 2)借用构造函数实现继承 **
/////////////////借用构造函数
function Human(name){
this.name = name;
}
function Man(name,sex){
// 采用对象冒充方式传参
Human.call(this,name);
this.sex = sex;
}
var man = new Man("张三丰99","男");
window.console.log(man.name + "," + man.sex);
这种继承解决了向超类传参的问题,但是因为没有使用原型,则就无法实现方法等共享问题
** 3)组合继承 **
/////////////////组合继承
function Human(name){
this.name = name;
}
Human.prototype.description = function(){
window.console.log(this.name + ",我来自于中国......");
}
function Man(name,sex){
// 采用对象冒充方式传参
Human.call(this,name);
this.sex = sex;
}
// Man的原型指向了Human
Man.prototype = new Human();
var man = new Man("张三丰88","男");
window.console.log(man.name + "," + man.sex);
man.description();
解决了向超类传参问题,使用了原型链,实现了方法共享。
** 4)原型式继承 **:不单独使用
// 临时中转函数
function transition(obj){
function F(){};
// 这里的F继承了obj
F.prototype = obj;
return new F();
}
// 字面量方式声明一个对象
var obj = {
name : "zhangsanfeng",
age : 100,
arr:["aa","bb"]
};
var obj1 = transition(obj);
obj1.arr.push("cc");
window.console.log(obj1.arr);// ["aa", "bb", "cc"]
var obj2 = transition(obj);
// 共享带来的问题:没法独立持有自己的信息了
window.console.log(obj2.arr);// 是["aa", "bb", "cc"]而不是["aa", "bb"]
** 5)寄生式继承 **:原生式+工厂模式
// 临时中转函数
function transition(obj){
function F(){};
// 这里的F继承了obj
F.prototype = obj;
return new F();
}
// 寄生函数
function create(o){
var f = transition(obj);
return f;
}
// 字面量方式声明一个对象
var obj = {
name : "zhangsanfeng",
age : 100,
arr:["aa","bb"]
};
var obj1 = create(obj);
obj1.arr.push("cc");
window.console.log(obj1.arr);
var obj2 = create(obj);
// 共享带来的问题:没法独立持有自己的信息了
window.console.log(obj2.arr);
** 6)寄生组合继承 **
// 临时中转函数
function transition(obj){
function F(){};
// 这里的F继承了obj
F.prototype = obj;
return new F();
}
// 寄生函数,子类的原型指向建立的中转对象,中转对象原型指向超类:原生链继承
function create(parent,child){
var f = transition(parent.prototype);
f.constructor = child; //
child.prototype = f; // child的原型指向了中转类
}
function Human(name){
this.name = name;
}
Human.prototype.run = function(){
window.console.log(this.name + "..............");
}
function Man(name,sex){
// 对象冒充,实现参数传递
Human.call(this,name);
this.sex = sex;
}
// 寄生组合继承
create(Human,Man);
var man = new Man("张三丰88","男");
man.run();
window.console.log(man.name + "," + man.sex);
5 匿名函数与闭包
// 匿名函数赋值给变量
var add = function(){
window.console.log("hhhhhhhhhhhhhhhhhhh");
};
add();
// 自运行 (匿名函数)()
(function(name){
window.console.log(name + "来自于中国。。。。。。。");
})("zhangsanfeng");
// 函数里面放置一个匿名函数
function userInfo (){
return function(){ // 闭包:在一个函数中创建另外一个函数,通过另外一个函数访问这个函数的局部变量
return "userInfo"
}
}
window.console.log(userInfo()());
function increase(){
var count = 0;
return function(){
count++;
return count;
}
}
window.console.log("count1 = " + increase()());
window.console.log("count2 = " + increase()());
window.console.log("count3 = " + increase()());
var f = increase();
// 闭包的优点也是缺点:闭包作用域里面的局部变量资源不会被立刻销毁,如果使用不当,会造成内存泄漏
window.console.log("count11 = " + f());
window.console.log("count12 = " + f());
window.console.log("count13 = " + f());
循环中的闭包
function arrCreate(){
var arr = [];
for(var i=0;i<5;i++){
// 匿名函数自我执行
arr[i] = (function(num){
return num;
})(i);
}
return arr;
}
var arr = arrCreate();
for(var i=0;i<5;i++){
window.console.log("arr[" + i + "] = " + arr[i]);
}
//////////////////////////////////////////////////////
function arrCreate(){
var arr = [];
for(var i=0;i<5;i++){
arr[i] = (function(num){
// 局部变量驻留在内存中
return function(){
return num;
};
})(i);
}
return arr;
}
var arr = arrCreate();
for(var i=0;i<5;i++){
window.console.log("arr[" + i + "] = " + arr[i]());
}
闭包引起的内存泄漏问题:
window.onload = function test(){
var testDiv = document.getElementById("testDiv");
testDiv.onclick = function(){
// 这里的testDiv将驻留在内存中,得不到及时的释放,将会引起内存泄漏
window.console.log("hhh-->" + testDiv.innerHTML);
}
};
模仿块级作用域
(function test () {
for (var i = 0; i < 5; i++) {
}
// 因为JavaScript中并没有块级作用域这种概念,所以这里的i是还能访问到的
// 即使这里再次声明了i,比如var i;也是没有作用的
window.console.log("i = " + i);
})();
(function test () {
(function(){ // 闭包模仿块级作用域
for (var i = 0; i < 5; i++) {
window.console.log("i = " + i);
}
})();
// 这里就访问不到i了
window.console.log("i = " + i);
})();
网友评论