初识对象
-
面向对象的特征
- 封装性、继承性、多态性
- 对象是键值对的集合:对象是由属性和方法构成的 (ps:也有说法为:对象里面皆属性,认为方法也是一个属性)
-
对象属性操作
var student = {
name: 'sunny',
age: 20,
number: '11034P',
department: 'newEnergy',
score: function(){
return '成绩查询系统!';
},
job: function(){
return '就业查询系统!';
}
}
var arr = [];
// .语法
// .语法后面不能使用js中的关键字、保留字(class、this、function。。。)
// .语法后面不能使用数字
// .语法后面可直接执行方法函数
console.log(student.name);
console.log(student.score());
// []语法
// []内部必须用引号引入变量名 (student['number']
// ["class"]、["this"]等保留字都可以随意使用
// [0]、[1]、[2]也可以使用 ? 为什么obj[3]==obj["3"]
// 甚至还可以这样用:["[object Array]"] jquery里面就有这样的实现
// 也可以这样用:["{abc}"] 给对象添加了{abc}属性
console.log(student['age']);
console.log(student['job']);
// 添加属性
student['[object Array]'] = 240;
student['{object}'] = {'arts':85, 'sports': 95};
for(key in student){
arr.push(key);
}
console.log(student);
console.log(arr);
arr = [];
// 修改属性
student.gender = 'man';
student['direction'] = function(){
return '人工智能与图形操作';
}
for(key in student){
arr.push(key);
}
console.log(student);
console.log(arr);
arr = [];
// 删除属性
delete student.number;
for(key in student){
arr.push(key);
}
console.log(student);
console.log(arr);
arr = [];
- 获取对象长度的方法
- 对象的长度不能用.length获取
- Object具有一个keys属性,可以返回json对象的key的数组
- Object可以使用in运算符
var student = {
name: 'sunny',
age: 20,
number: '11034P',
department: 'newEnergy',
score: function(){
return '成绩查询系统!';
},
job: function(){
return '就业查询系统!';
}
}
var arr = [];
arr = Object.keys(student);
console.log(arr.length);
arr = [];
for(key in student){
arr.push(key);
}
console.log(arr.length);
arr = [];
console.log('name' in student);
- 对象的浅拷贝与深拷贝
// 浅拷贝
var student = {
name: 'sunny',
age: 20,
number: '11034P',
department: 'newEnergy',
score(){
return '成绩查询系统!';
},
job(){
return '就业查询系统!';
}
}
var simple = obj=>{
let newObj = {};
for(let key in obj){
newObj[key] = obj[key];
}
return newObj;
}
var sunny = simple(student);
console.log(sunny);
// 深拷贝
var student = {
name: 'sunny',
age: 20,
number: '11034P',
department: 'newEnergy',
score: {
arts: 95,
sports: 85,
program: 95
},
job(){
return '就业查询系统!';
}
}
var deepCopy = obj=>{
var newObj = {};
for(let key in obj){
newObj[key] = (typeof obj[key] === 'object') ? deepCopy(obj[key]) : obj[key];
}
return newObj;
}
var sunny = deepCopy(student);
console.log(sunny);
构造函数
-
构造函数的概念
- 任何函数都可以当成构造函数
- 只要把一个函数通过new的方式来进行调用,我们就把这一次函数的调用方式称之为:构造函数的调用
- new CreateFunc(); 此时CreateFunc就是一个构造函数
- CreateFunc(); 此时的CreateFunc并不是构造函数
- new Object()等同于对象字面量{}
-
构造函数的执行过程
var p1=new Person();
- 创建一个对象 (我们把这个对象称之为Person构造函数的实例)-
_p1
- 创建一个内部对象,
this
,将this指向该实例(_p1) - 执行函数内部的代码,其中,操作this的部分就是操作了该实例(_p1)
- 返回值:
- 如果函数没有返回值(没有return语句),那么就会返回构造函数的实例(_p1)
- 如果函数返回了一个基本数据类型的值,那么本次构造函数的返回值是该实例(_p1)
- 如果函数返回了一个引用数据类型的值,那么本次函数的返回值就是该值
-
创建构造函数
var student = {};
student.name = 'sunny';
- 内置的构造函数
var obj = new Object();
var num = new Number();
var str = new String();
var now = new Date();
var arr = new Array();
var reg = new RegExp();
var bool = new Boolean();
var func = new Function();
var img = new Image();
console.log(obj);
console.log(obj.constructor);
console.log(num);
console.log(num.constructor);
console.log(str);
console.log(str.constructor);
console.log(now);
console.log(now.constructor);
console.log(arr);
console.log(arr.constructor);
console.log(reg);
console.log(reg.constructor);
console.log(bool);
console.log(bool.constructor);
console.log(func);
console.log(func.constructor);
console.log(img);
console.log(img.constructor);
- 自定义构造函数
- 构造函数的命名推荐采用帕斯卡命名规则,即所有的单词首字母大写。
- 在构造函数内部,使用this来表示刚刚创建的对象。
var Computer = function(memory, storage, videoMemory, run){
this.memory = memory;
this.storage = storage;
this.videoMemory = videoMemory;
this.run = run;
this.introduce = function(){
return this.memory;
}
}
var noteBook = {
memory: '16GB',
storage: '2TB',
videoMemory: '4GB',
run: function(){
return '你好,世界!';
}
}
var Dell = new Computer(noteBook.memory, noteBook.storage, noteBook.videoMemory, noteBook.run);
console.log(Dell);
console.log(Dell.run());
console.log(Dell.introduce());
- 私有成员
- 在构造函数中,使用var关键字定义的变量称为私有成员
- 在实例对象后无法通过“对象成员”的方式进行访问
- 但是私有成员可以在对象的成员方法中访问
var Computer = function(memory, storage, videoMemory, run){
var processor = 'Core i7-4700MQ';
this.memory = memory;
this.storage = storage;
this.videoMemory = videoMemory;
this.run = run;
this.introduce = function(){
return this.memory + processor;
}
}
var noteBook = {
memory: '16GB',
storage: '2TB',
videoMemory: '4GB',
run: function(){
return '你好,世界!';
}
}
var Dell = new Computer(noteBook.memory, noteBook.storage, noteBook.videoMemory, noteBook.run);
console.log(Dell);
console.log(Dell.processor);
console.log(Dell.run());
console.log(Dell.introduce());
- 构造函数中的return关键字
- 构造函数中,return返回一个数组或对象等引用类型数据,则构造函数会直接返回该数据,而不会返回原来创建的对象。
- 如果返回的是基本类型数据,则返回的数据无效,依然会返回原来创建的对象。
- 总结:构造函数中的return只能返回引用类型的数组或对象,有合规的返回值返回数据,没有就返回本身的对象数据
// 返回值是引用类型数据,返回对象本身
var Computer = function(memory, storage, videoMemory, run){
var processor = 'Core i7-4700MQ';
this.memory = memory;
this.storage = storage;
this.videoMemory = videoMemory;
this.run = run;
this.introduce = function(){
return this.memory + processor;
}
return {say:'你好,世界!'};
}
var Lenovo = new Computer();
console.log(Lenovo);
// 返回值是基本类型数据,返回数据无效,返回原来创建的对象
var Computer = function(memory, storage, videoMemory, run){
var processor = 'Core i7-4700MQ';
this.memory = memory;
this.storage = storage;
this.videoMemory = videoMemory;
this.run = run;
this.introduce = function(){
return this.memory + processor;
}
return 0;
}
var Lenovo = new Computer();
console.log(Lenovo);
内置对象
- 定义:内置对象就是指这个语言自带的一些对象,供开发者使用,这些对象提供了一些常用的或是最基本而必要的功能。
Arguments
函数参数集合Array
数组Boolean
布尔对象Date
日期时间Error
异常对象Function
函数构造器Math
数学对象Number
数值对象Object
基础对象RegExp
正则表达式对象
String对象
- String()对象
length
- 获取字符串的长度charAt(index)
- 获取index位置的字符,位置从0开始计算indexOf(searchValue)
- 获取searchValue在字符串中首次出现的位置,没有返回-1lastIndexOf(searchValue)
- 获取searchValue在字符串中最后出现的位置,没有返回-1substring(start[,end])
- 截取从start位置到end位置之间的一个子字符串substr(start[,length])
- 截取从start位置开始到length长度的子字符串toLowerCase()
- 获取字符串的小写形式toUpperCase()
- 获取字符串的大写形式split(separator[,limit])
- 使用separator分隔符将字符串分割成数组,limit用于限制数量replace(str1,str2)
- 使用str2替换字符串中的str1,返回替换结果
var userName = function(name){
var flag = 0;
return function(){
name.length<3 || name.length>15 ? console.log('用户名长度必须在3~10之间') :
name.toLowerCase().indexOf('admin') !== -1 ? console.log('用户名中不能出现"admin"敏感词') :
flag = 1;
return flag;
}
}
var newName = userName('dministrator');
console.log(newName());
Number对象
- Number()对象
MAX_VALUE
- 在JavaScript中所能表示的最大数值(静态成员)MIN-VALUE
- 在JavaScript中所能表示的最小正值(静态成员)toFixed(digits)
- 使用定点表示法来格式化一个数值
var num = 12345.6789;
num.toFixed(); // 12346
num.toFixed(1); // 12345.7
num.toFixed(6); // 12345.678900
Number.MAX_VALUE; // 1.7976931348623157e+308
Number.MIN_VALUE; // 5e-324
Math对象
- Math()对象:专门封装数学计算常用常量和计算方法的全局对象,Math没有构造函数,不能new!所有API都直接用Math.xxx,
Math.PI()
- 获取圆周率,结果为3.141592653589793Math.abs()
- 获取x的绝对值,可传入普通数值或是用字符串表示的数值Math.max([value1[,value2,...]])
- 获取所有参数中的最大值Math.min([value1[,value2,...]])
- 获取所有参数中的最小值Math.pow(base,exponent)
- 获取基数(base)的指数(exponent)次幂Math.sqrt(x)
- 获取x的平方根Math.ceil(x)
- 获取大于等于x的最小整数,即向上取整Math.floor(x)
- 获取小于等于x的最大整数,即向上取整Math.round(x)
- 获取x的四舍五入后的整数值Math.random()
- 获取大于或等于0.0且小于1.0的随机数
// 随机数函数
var randomNum= function(min, max){
if(arguments.length== 2){
return Math.floor(Math.random()* (max- min+ 1)+ min);
// return parseInt(Math.random()* (max- min+ 1)+ min);
}else if(arguments.length== 1){
return Math.ceil(Math.random()* min);
}else{
return Math.ceil(Math.random()* 100);
}
}
console.log(randomNum());
// 获得数组最大值
Math.max.apply(Math,[1,2,3,4]);
Date对象
- Date()对象:封装一个1970年元旦至今的毫秒数(从1970年1月1日0点0分0秒到当前时间的毫秒差),提供了对时间操作的方法
- get方法用来获得分量的值
getFullYear()
- 获取表示年份的数字,如2020getMonth()
- 获取月份,范围0~11(0表示一月,1表示二月,以此类推)getDate()
- 获取月份中的某一天,范围1~31getDay()
- 获取星期,范围0~6(0表示星期一,1表示星期二,以此类推)getHours()
- 获取小时数,返回0~23getMinutes()
- 获取分钟数,范围0~59getSeconds()
- 获取秒数,范围0~59getMilliseconds()
- 获取毫秒数,范围0~999getTime()
- 获取从1970-01-01 00:00:00 距离Date对象所代表时间的毫秒数
- set方法用来设置分量的值
setFullYear(value)
- 设置年份setMonth(value)
- 设置月份setDate(value)
- 设置月份中的某一天setHours(value)
- 设置小时数setMinutes(value)
- 设置分钟数setSeconds(value)
- 设置秒数setMilliseconds(value)
- 设置毫秒数setTime(value)
- 通过从1970-01-01 00:00:00 计时的毫秒数来设置时间
- get方法用来获得分量的值
- 日期转字符串
var date = new Date();
date.toString()
- GMT格式显示date.toLocaleString()
- 操作系统当地时间格式,包含时间和日期date.toLocaleDateString()
- 以操作系统当地时间格式,仅包含日期date.toLocaleTimeString()
- 以操作系统当地时间格式,仅包含时间
- Date计算
- 两日期对象可直接相减,结果是毫秒差!
- 对每个分量做加减:3步: get分量值,做加减,set回去
- 一步概括:date.setXXX(date.getXXX()+/-n);
// 当前系统时间
var date = new Date();
// 2012年10月13日12时26分35秒
var date = new Date(2012, 10, 13, 12, 26, 35);
// 2012年10月有多少天
var max = new Date(2012, 10, 0).getDate();
// 获取当前月份
var nowMonth = new Date().getMonth()+ 1;
// 将毫秒数转化为时间
var date = new Date(1499996760000);
var dateTime = date.toLocaleString();
// 1970年1月1日午夜以来的毫秒数
new Date().getTime(); // 提倡使用的
new Date().valueOf();
Date.now(); // 直接使用的
+new Date(); // 相当于隐式转换,将该元素转换成Number类型,如果转换失败,那么将得到 NaN
new Date()* 1; // +new Date() 将会调用 Date.prototype 上的 valueOf() 方法,根据MDN,Date.prototype.value方法等同于Date.prototype.getTime()
// 实时时间
setInterval(
_=> {
var date = new Date();
var formate = (date=> {
var week = ['日', '一', '二', '三', '四', '五', '六'];
return date.getFullYear()+ '年'+
(date.getMonth()+ 1)+ '月'+
date.getDate()+ '日'+' ' +
'星期'+ week[date.getDay()]+ ' '+
date.toLocaleTimeString();
})(date);
console.log(formate);
},1000);
错误处理与代码调试
-
错误类型
Error
- 表示普通错误,其余6种类型的错误对象都继承自该对象EvalError
- 调用eval()函数错误,已经弃用,为了向后兼容,低版本还可以使用RangeError
- 数值超出有效范围,如"new Array(-1)"ReferenceError
- 引用了一个不存在的变量,如"var a=1;a+b;"(变量b未定义)SyntaxError
- 解析过程语法错误,如"{;}" "if()" "var a=new;"TypeError
- 变量或参数不是预期类型,如调用了不存在的函数或方法URIError
- 解析URI编码出错,调用encodeURI()、escape()等URI处理函数时出现
-
错误处理:发生错误时,保证程序不中断
try{
可能发生错误的代码
}catch(err){//err中发生错误时会自动收到创建的error对象
err.message: 保存了错误的详细原因
err.name: 保存了错误对象的名称
如果发生错误,才执行的错误处理代码
}[finally{
无论是否发生错误,都执行的代码
*一定会在退出*之前*执行*
一般释放用过的对象资源:xxx=null
}]
- 手动抛出错误对象
try{
// 创建错误对象
var err = new Error('自定义错误信息');
// 抛出错误对象
// 也可以与上一行合并为:throw new Error('自定义错误信息');
throw err;
}catch(err){
// 输出结果,自定义错误信息
console.log(err.message);
}
-
执行效率
- 如果可以提前预知错误的原因:建议使用if代替try catch
- try中尽量少的包含代码,try的执行效率低,且多创建一个error对象
-
Source面板调试工具
Watch
- 可以对加入监听列表的变量进行监听Call Stack
- 函数调用堆栈,可以在代码暂停时查看执行路径Scope
- 查看当前断点所在函数执行的作用域内容Breakpoints
- 查看断点列表XHR Breakpoints
- 请求断点列表,可以对满足过滤条件的请求进行断点拦截DOM Breakpoints
- DOM断点列表,设置DOM断点后满足条件时触发断点Global Listeners
- 全局监听列表,显示绑定在window对象上的事件监听Event Listener Breakpoints
- 可断点的事件监听列表,可以在触发事件时进入断点
原型和原型链
-
原型
- 原型(prototype):保存所有子对象共有成员的对象
- 每个构造函数都有一个原型属性,引用了该构造函数对应的原型对象
- 由构造函数创建的每个对象中都有一个__proto__属性,指向构造函数的原型对象
- 在访问子对象的成员时,优先在成员本地找,找不到,再去构造函数的原型中查找
-
创建原型
- 创建空对象
- 调用构造函数,为新对象添加成员
- 设置当前对象的__proto__属性为构造函数的原型对象
- 每个对象自有的成员,放在构造函数中
- 所有子对象共有的成员,放在原型对象中
-
原型链
- 由各级对象的__proto__逐级向上引用形成的多级继承关系
- 所有的对象都有一个__proto__属性,指向自己的父对象
-
原型相关API
- 判断一个属性是否可用
- in关键字:
"属性名" in 对象
- 如果"属性名"在当前对象的原型链中,返回true
- 如果在整条原型链上都没找到,返回false
- 使用===undefined:说明不包含
- 简写为
(!对象.成员名)
- 简写为
- 判断是否自有属性:
obj.hasOwnProperty("成员");
- 如果obj本地有指定"成员",则返回true,没有返回false
- 判断共有属性:
- 不是自有的,还要存在于原型链上
if(!obj.hasOwnProperty("成员")&&"成员" in obj)
- in关键字:
- 获得任意对象的__proto__属性:
- 获得父级对象
var 父对象=Object.getPrototypeOf(对象);
- 判断父对象是否处在子对象原型链的上级:
父对象.isPrototypeOf(子对象);
- 只要父对象在子对象的原型链上,就返回true,否则返回false
- 删除对象中的属性:
delete 对象.成员
- 只能删除自有的成员
- 只有var声明的全局变量不让delete
- 使用window.或window[""]增加的全局成员可以delete
- 判断一个对象是数组的几种方法
Array.prototype.isPrototypeOf(obj)
- 如果返回true,说明是数组,否则不是
obj instanceof Array
- 对象 instanceof 类型名,返回true,说明是数组
- instancof会查找原型链上所有原型的构造函数
Object.getPrototypeOf(obj)==Array.prototype
Object.prototype.toString.call(arr)
- override,子对象觉得父对象的成员不好用
- 可自己定义同名的自有成员覆盖父对象
Object.prototype.toString.apply(arr)
- call和apply: 在调用方法的一瞬间,替换调用方法的对象
Array.isArray(obj)
- 专门判断对象是否是数组!
- 判断一个属性是否可用
继承
-
概念
- 通过【某种方式】让一个对象可以访问到另一个对象中的属性和方法,我们把这种方式称之为继承
并不是所谓的xxx extends yyy
- 有些对象会有方法(动作、行为),而这些方法都是函数,如果把这些方法和函数都放在构造函数中声明就会导致内存的浪费
- 通过【某种方式】让一个对象可以访问到另一个对象中的属性和方法,我们把这种方式称之为继承
-
继承的第一种方式:原型链方法继承
var Person = function(){
};
Person.prototype.say=function(){
console.log("你好")
}
var sunny = new Person();
console.log(sunny.say());
- 继承的第二种方式:原型链继承
// 一般情况下,应该先改变原型对象,再创建对象;
// 一般情况下,对于新原型,会添加一个constructor属性,从而不破坏原有的原型对象的结构;
var Person = function(){};
Person.prototype = {
//切记不能忘记
constructor:Person,
say:function(){
console.log("你好");
},
run:function(){
console.log("正在进行百米冲刺");
}
}
var sunny = new Person;
sunny.say();
console.log(Person.prototype.constructor === Person);
- 继承的第三种方式:拷贝继承(混入继承:mixin)
// 由于拷贝继承在实际开发中使用场景非常多,所以很多库都对此有了实现
// jquery:$.extend
var Person = {
hear: 'black',
skin: 'yellow',
eye: 'brown',
}
var extend = function (source){
let target = {};
for(let key in source){
target[key] = source[key];
}
return target;
}
var sunny = extend(Person);
sunny.hear = 'white';
console.log(sunny);
// es6中有了 <对象扩展运算符> 仿佛就是专门为了拷贝继承而生
var Person = {
hear: 'black',
skin: 'yellow',
eye: 'brown',
}
var target = {...Person};
console.log(target);
- 继承的第四种方式:原型继承 (道格拉斯在蝴蝶书中提出来的)
var Person = {
hear: 'black',
skin: 'yellow',
eye: 'brown',
}
var sunny = Object.create(Person);
console.log(sunny.__proto__);
- 继承的第五种方式:借用构造函数实现继承
var Animal = function(name, age){
this.name = name;
this.age = age;
}
var Person = function(name, age, address){
Animal.call(this, name, age);
this.address = address;
return this.age;
}
console.log(Person('dog', '18', '不告诉你!'));