function fn() {
// 在每一个函数中,都有一个隐含的参数,叫做 this
// this代表的是当前调用函数的对象,谁调用的函数this就是谁
// this是谁,主要由函数的调用方式来决定:
// 1.如果以函数的形式去调用,this就是window
// (xxx() yyy())
// 2.如果以方法的形式去调用,this就是调用方法的对象
// (aaa.bbb() xxx.yyy())
console.log(this);
}
var obj = {
name:'swk',
sayHello:fn
}
var obj2 = {
name:'zbj',
sayHello:fn
}
window.fn();
// obj2.sayHello();
// obj.sayHello();
//---this 以构造函数形式调用时,this是新创建的那个对象
//-- 以箭头函数调用时—this是包含着它的外层函数对象
函数的this只受函数调用方式的影响,谁调用this就是谁
//-除了- 以箭头函数调用时—this是包含着它的外层函数对象
var name = 'hello';
var obj = {
name:'孙悟空',
sayName:function () {
console.log(this.name);
}
};
// obj.sayName();
var fn = obj.sayName;
// fn();
var obj2 = {
name:'猪八戒',
sayName:function () {
console.log(this.name);
//调用全局函数fn
// fn();
// 定义一个内部函数
// this是谁和函数的定义方式无关,只和调用方式有关
function t() {
console.log(this);
}
t();
}
}
obj2.sayName();
这种创建对象函数,被称为工厂方法
//--方法-1----
// function createPerson(name, age, gender) {
// //创建一个对象
// var o = {};
//
// //向对象中添加属性
// o.name = name;
// o.age = age;
// o.gender = gender;
// o.sayHello = function () {
// console.log('大家好,我是'+this.name);
// };
//
// //将新的对象作为返回值返回
// return o;
// }
//------------2----------省略var o = {}; return o;--
// function createPerson(name, age, gender) {
// //将新的对象作为返回值返回
// return {
// name:name,
// age:age,
// gender:gender,
// sayHello() {
// console.log('大家好,我是'+this.name);
// }
// };
// }
//-----------------3--省略name:name-age:age-gender:gender--
function createPerson(name, age, gender) {
//将新的对象作为返回值返回
return {
name,
age,
gender,
sayHello() {
console.log('大家好,我是'+this.name);
}
};
}
var p1 = createPerson('孙悟空', 18, '男');
var p2 = createPerson('猪八戒', 28, '男');
var p3 = createPerson('沙和尚', 38, '男');
// console.log(p1);
// console.log(p2);
// console.log(p3);
p1.sayHello();
p2.sayHello();
p3.sayHello();
//注意!:如果构造函数写了return 返回值是对象形式(数组,对象,函数)则 new实例只能拿到这个返回值对象, return 其它基本类型的值 则不影响实例
构造函数(constructor)
- 构造函数是专门用来创建对象的函数
- 构造函数其实就是一个普通函数
- 构造函数和普通函数的主要区别在于它们的调用方式
如果函数直接调用,那么它就是一个普通函数
如果通过new关键字来调用,那么它就是一个构造函数
- 为了和普通函数区分构造函数通常会大写字母开头
- 构造函数的执行流程:
1.调用构造函数,它会创建一个新的对象
2.将新的对象设置为函数中的this
3.执行函数中的代码
4.将新的对象作为返回值返回
// function Person(name,age,gender) {
// this.name=name
// this.age=age
// this.gender=gender
// this.saynane=function(){
// console.log("say my name" + this.name)
// }
// }
// var per1= new Person('SWK',18,'男')
// var per2= new Person('SWf',18,'男')
// var per3= new Person('SWt',18,'男')
// console.log(per1)
// console.log(per2)
// console.log(per3)
// console.log(per1 instanceof Person)
// console.log(Person instanceof per1) //会报错,instanceof后面需要是一个类名
目前,我们将sayHello()方法定义在了构造函数的内部,
这样意味着,构造函数每执行一次就会创建一个新的函数对象
这就导致了,每一个对象的sayHello()方法都是不同的函数对象
但是本质上每个对象sayHello()结构是一模一样的,
创建多个对象完全是浪费内存空间
创建这么多相同的函数对象是没有必要的,
我们完全可以让所有的对象都使用同一个sayHello()函数
问题:如何能够做到这件事呢?
//创建一个Person类的实例
var p1 = new Person('孙悟空');
var p2 = new Person('渣渣辉');
// p1.sayHello();
// p2.sayHello();
console.log(p1.sayHello === p2.sayHello);
function Person(name) {
this.name = name;
this.sayHello = fn;
}
// 将函数设置为全局函数
// 将函数定义在全局作用域确实可以解决问题,
// 但与此同时全局函数也污染到了全局的命名空间(namespace)
function fn() {
console.log('Hello,大家好,我是 '+this.name);
}
var p1 = new Person('孙悟空');
var p2 = new Person('渣渣辉');
console.log(p1.sayHello === p2.sayHello);
p1.sayHello();
p2.sayHello();
原型(prototype)
- 每一个函数中都会有一个属性prototype,它指向的是一个对象
这个对象我们称为“原型对象”
- 如果函数是作为普通函数调用的,则prototype没有任何作用
- 如果函数作为构造函数调用,那么通过该构造函数所创建的实例中,
都会有一个隐藏属性指向该原型对象,可以通过对象.__proto__来访问该属性
- 原型对象就相当于一个公共的区域,可以被所有的实例访问到,
当我们访问对象中的属性时,会先在对象本身中寻找
如果找到了则直接使用,如果没有找到则去原型对象中寻找
如果找到了则使用如果没有找到则去原型对象中寻找
如果找到了则使用,如果原型对象中依然没有
则去原型对象的原型对象中寻找,以此类推..
直到找到Object的原型,Object的原型是所有对象原型
如果依然没有找到,则返回undefined
这就形成了一条原型链,搜索对象的属性时是沿着原型链向上寻找
和作用域链不同,在作用域中找一个变量时,如果没有会报错
而在原型链中找一个属性时,如果没有会返回undefined
- 我们可以将对象中共有的属性,统一存储到原型对象中,
这样我们只需创建一个属性,即可被该类的所有实例共享
- 我们可以将对象中共有的属性,统一存储到原型对象中,
这样我们只需创建一个属性,即可被该类的所有实例共享
function Person(name) {
this.name = name;
}
// 将sayHello设置到Person的prototype中
Person.prototype.sayHello = function () {
console.log('Hello,大家好,我是 '+this.name);
};
var p1 = new Person('孙悟空');
var p2 = new Person('渣渣辉');
p1.sayHello();
p2.sayHello();
console.log(p1.sayHello === p2.sayHello);
//获取一个对象的原型
// 可以通过实例的__proto__属性,或类的prototype属性来获取一个实例的原型对象
// p1.__proto__
// Object.getPrototypeOf(p1);
// Person.prototype.__proto__
// --Person.__proto__ 构造函数自己的原型,(类)自己用的
// -- Person.prototype 实例的原型,给实例用的
// Object的原型 Object.prototype 是所有对象的原型
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.sayHello = function () {
console.log('大家好,我是 '+this.name);
};
Person.prototype.abc = '嘻嘻';
var p1 = new Person('孙悟空', 18);
// console.log(p1.abc);
//in 运算符,用来检查一个对象中是否含有某个属性
//使用in检查一个属性时,无论属性是在原型中还是在对象本身中都会返回true
// console.log('abc' in p1);
// hasOwnProperty() 用来检查一个属性是否在对象自身中
// console.log(p1.hasOwnProperty('abc'));
// console.log(p1.__proto__.hasOwnProperty('abc'));
function MyClass() {
}
var mc = new MyClass();
console.log(mc.__proto__);
console.log(mc.__proto__.__proto__);
console.log(mc.__proto__.__proto__.__proto__);//找到Object原型后再向上找原型会返回null
document.write(Person.__proto__.__proto__.__proto__)//查看对象的原型时,找到Object原型后再向上找会返回null
console.log(mc.abc);//找属性时 在该对象和原型中都没找到该属性,则返回undefined
//Object的原型 Object.prototype 是所有对象的原型
console.log(mc instanceof MyClass);//true
console.log(mc instanceof Object);//true
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.sayHello = function () {
console.log('大家好,我是 '+this.name);
};
// 打印一个对象时,显示的实际上对象的toString()方法的返回值
// 如果不希望打印[object Object] 可以重新为对象设置一个toString()方法
Person.prototype.toString = function () {
return 'Person [name: '+this.name+', age: '+this.age+']';
};
var p1 = new Person('孙悟空', 18);
// Person.__proto__ 构造函数自己的原型,自己用的
// Person.prototype 实例的原型,给实例用的
//获取一个对象的原型
// 可以通过实例的__proto__属性,或类的prototype属性来获取一个实例的原型对象
// p1.__proto__
// Object.getPrototypeOf(p1);
// Person.prototype.__proto__
var p2 = new Person('猪八戒', 28);
alert(p2);
垃圾回收(GC Garbage Collect)
- 程序在运行的过程中,会产生出大量的垃圾
这些垃圾如果长期存在于内存中,会严重影响到程序的运行的速度
程序中的垃圾必须被及时清理,以确保运行不会影响到程序的运行
- 在程序中,所有访问不到的对象就属于垃圾对象
- 如果一个对象没有任何的变量(属性)对其进行引用则该对象就是一个垃圾对象
- 像这种垃圾对象,在程序存在过多,会导致程序运行变慢,必须清理
在JS中有自动的垃圾回收机制,我们不需要手动的处理垃圾
只需将不再使用的变量设置为null即可
var a = {}; a = null;
- 引用计数:
- 引用计数会带来循环引用的问题,导致一些垃圾不能被正确的回收。a = null;
- 标记清除:
- 标记清除需要遍历所有的对象,所以它的性能会比较差
- 分代收集:
- 将对象分成两代或三代,一代表示新创建的对象,二代和三代表示老的对象
- 一代对象检查的频率最高,二代对象次之,三代最少
构造函数显示原型 指向 构造函数原型对象
构造函数隐式原型 指向 Function原型对象
构造函数原型对象的构造器 指向 构造函数
构造函数原型对象的隐式原型 指向 Object原型对象
构造函数的实例对象 隐式原型 指向 构造函数原型对象
Object对象显示原型 指向 Object原型对象
Object对象隐式原型 指向 Function原型对象
Object原型对象的构造器 指向 Object对象
Object原型对象的隐式原型 指向 null
Object的实例对象的隐式原型 指向 Object原型对象
Function对象的显示/隐式原型 指向 Function原型对象
Function原型对象的构造器 指向 Function对象
Function原型对象的隐式原型 指向 Object原型对象
欢迎关注我的原创文章:小伙伴们!我是一名热衷于前端开发的作者,致力于分享我的知识和经验,帮助其他学习前端的小伙伴们。在我的文章中,你将会找到大量关于前端开发的精彩内容。
学习前端技术是现代互联网时代中非常重要的一项技能。无论你是想成为一名专业的前端工程师,还是仅仅对前端开发感兴趣,我的文章将能为你提供宝贵的指导和知识。
在我的文章中,你将会学到如何使用HTML、CSS和JavaScript创建精美的网页。我将深入讲解每个语言的基础知识,并提供一些实用技巧和最佳实践。无论你是初学者还是有一定经验的开发者,我的文章都能够满足你的学习需求。
此外,我还会分享一些关于前端开发的最新动态和行业趋势。互联网技术在不断发展,新的框架和工具层出不穷。通过我的文章,你将会了解到最新的前端技术趋势,并了解如何应对这些变化。
我深知学习前端不易,因此我将尽力以简洁明了的方式解释复杂的概念,并提供一些易于理解的实例和案例。我希望我的文章能够帮助你更快地理解前端开发,并提升你的技能。
如果你想了解更多关于前端开发的内容,不妨关注我的原创文章。我会不定期更新,为你带来最新的前端技术和知识。感谢你的关注和支持,我们一起探讨交流技术共同进步,期待与你一同探索前端开发的奇妙世界!