对象是从我们现实生活中抽象出来的一个概念,俗话说物以类聚,人以群分,我们也经常说有一类人,他们专业给隔壁家制造惊喜,也就是我们说的老王。
这里面就有两个重要概念
类:无论是物以类聚,还是有一类人,这里说的类并不是实际存在的事物,是一些特征、是一些规则等。
老王:这是个实物,是现实存在,和类的关系就是符合类的描述
对应到计算机术语,类就是class,定义了一些特点(属性 property)和行为(方法 method),比如说给隔壁制造惊喜的这类人有几个特征:
长相文质彬彬,为人和善;
姓王;
同时这些人还有技能(行为):
帮隔壁修下水道;
亲切问候对方儿子;
符合这些特点并且有上述行为能力的,我们称之为老王。从描述我们就可以看出来LaoWang不是指某个人,而是指一类人,符合上述描述的都可能是老王!用计算机术语说就是没个活蹦乱跳的老王都是类LaoWang。
一、创建对象
创建方式
1、使用new方式,使用typeof可以查看数据类型。
//创建person对象
var person = new Object();
//查看数据类型
console.log(typeof person);
2、使用{}方式
//创建person对象
var person = {};
// 创建对象
var person = {
name: "3D brother",
age: 81,
show: function () {
console.log(this.name, this.age);
}
};
//给person对象添加gender(性别)属性
person.gender = "man";
3、构造函数
构造函数调用时和普通函数不同,需要新建一个对象,然后才能调用,此特性需要注意。
// 创建Perosn的构造函数
function Person(name, age) {
// 设置属性
this.name = name;
this.age = age;
// 设置方法
this.show = function () {
console.log(this.name, this.age);
};
}
// 使用构造函数创建对象
var person2 = new Person("laowang", 20);
//使用Person的name属性
console.log(person2.name);
// 用show()方法展示
person2.show();
此方法将直接创建perosn对象,并直接在里面添加属性和方法。
4、工厂设计模式
所谓工厂设计模式,即在构造函数内新建一个对象,然后给对象添加属性和方法,然后按指定属性和方法返回一个新建对象。它调用方法和普通函数类似。
// 工厂设计模式
function people(name, age) {
var obj = new Object();
obj.name = name;
obj.age = age;
obj.show = function () {
console.log(this.name, this.age);
};
return obj;
}
//工厂设计模式和构造函数的区别就在于new关键字
var p = new Person("name", 17); // 构造函数调用需要新建对象
var p2 = people("name", 19);
添加属性/函数(即方法,类似于数组中的arr.length)
注意对象和JSON的区别。
添加方式:
1、. 的方式添加属性
//创建person对象
var person1 = new Object();
// var person2 = {};
// 给person对象添加属性
person1.name = 'laowang';
person1.gender = 'men';
person1.age = '20';
console.log(person);
Object即为对象,name(姓名),gender(性别),age(年龄),即为person对象的属性。
2、添加函数(即添加方法)
//创建person对象
var person = new Object();
// 给person对象添加属性
person.name = 'laowang';
person.gender = 'men';
person.age = '20';
// 给对象添加函数(方法),类似于数组中arr.length
person.sayHello = function () {
// 在函数内部可以使用this,谁调用这个函数,this就代表谁
console.log(this.name,this.gender,this.age);
}
person.sayHello();
由上面代码可以看出我们给person对象添加sayHello()方法,然后利用此方法就可以直接打印出隔壁老王的姓名、性别和年龄。
3、原型链的方式
二、JS的原型链
Js所有的函数都有一个prototype属性,这个属性引用了一个对象,即原型对象,也简称原型。这个函数包括构造函数和普通函数,我们讲的更多是构造函数的原型,但是也不能否定普通函数也有原型。所谓原型链,即原型所有的属性,其下所有子‘类’也具有相同属性,由此往下继承即为原型链。
作用:可以给‘类’添加属性和方法
1、Array.prototype.index = 10;
2、Array.prototype.show = function () {};
// 构造函数
function Person(name, age) {
this.name = name;
this.age = age;
this.show = function () {
console.log(this.name, this.age);
}
}
// 创建对象
var p = new Person('2d', 12);
// Person.prototype拿到原型,给原型设置属性
Person.prototype.gender = "haha";
// 拿到Object原型,设置属性,所有人都有index属性
Object.prototype.index = 1345;
// 拿到Object原型,设置属性,也可以添加方法show(),谁调用就打印谁。
Object.prototype.show = function () {
console.log(this);
};
console.log(p.gender);
console.log(p.index);
// 用Object的show()方法打印flag和数组
var flag = true;
flag.show();
var arr = [24, 24];
arr.show();
三、继承
让一个‘类’拥有另一个‘类’的属性和方法。
此处有三点需要注意:
1、call()和apply()方法的用法
语法:call([thisObj[,arg1[, arg2[, [,.argN]]]]])
定义:调用一个对象的一个方法,以另一个对象替换当前对象。
apply方法:
语法:apply([thisObj ,arguments)
定义:应用某一对象的一个方法,用另一个对象替换当前对象。
call, apply方法都是用于改变this的指向。区别是,从第二个参数起, call方法参数将依次传递给借用的方法作参数, 而apply直接将这些参数放到一个数组中再传递, 最后借用方法的参数列表是一样的.
// 构造函数
function Person (name,age) {
this.name = name;
this.age = age;
this.show = function () {
console.log(this.name,this.age);
};
};
//让student继承Person
function Student(name,age,major) {
// 让this调用Person构造函数,name/age作为参数传递;
// Person.call(this,name,age);
Person.apply(this,arguments);
// 处理自己特殊的属性和方法
this.major = major;
this.show = function () {
console.log(this.name,this.age,this.major);
};
}
// 新建Student对象,传递参数
var stu = new Student('laowang',19,'PS');
console.log(stu.name,stu.age,stu.major);
stu.show();
上述代码中person对象的属性,我们可以通过call()或者apply()方法改变this的指向,使Stuend对象来继承person的属性。
2、方法调用的顺序
我们构造一个函数,然后给它的原型添加属性,它的子类会继承它本身的属性而不会,继承它原型的属性;如果一定要子类继承它原型的属性必须把父类的原型赋给子类的原型。
// 构造函数Person
function Person(name, age){
this.name = name;
this.age = age;
this.show = function () {
console.log(this.name, this.age);
}
}
// 给Person的原型添加gender属性
Person.prototype.gender = "man";
// 构造函数
function Student(name, age, major) {
// 让Student继承Person的属性,将所有参数作为集合进行传递
Person.apply(this,arguments)
// 处理自己特殊的属性和方法
this.major = major;
this.show = function () {
console.log(this.name, this.age, this.major);
}
}
// 将Person的原型赋给Student的原型
Student.prototype = Person.prototype;
// 调用Person的属性
var p = new Person('laowang',20);
console.log(p.gender);
// 调用Student的属性
var student = new Student('xiaoming', 18, 'CAD');
student.show()
// 如果未将父级的原型赋给子级的原型此处将是undefined
console.log(student.gender);
上述代码中我们构造Person函数后,给它的原型添加gender(性别)属性,它的子级Student虽然继承了它的所有属性,但是却没有继承gender属性,为了使Student继承gender属性,我们需要把Person的原型赋给Student的原型,代码执行效果如下:
当父类的原型、父类、子类的原型和子类都具有一个相同的属性时,优先调用子类的属性;调用属性是如果子类没有该属性,会从其父类中查找,如果父类没有,就从子类的原型中查找,如果子类的原型中也没有就从父类的原型中调用,如果父类的原型中也没有(注意:要从父类的原型中查找首先要将父类的原型赋给子类的原型),就从Object中查找。
// 给Object的原型添加方法
Object.prototype.show = function () {
console.log("Object");
}
function Obj1(){
this.show = function () {
console.log('Obj1');
}
}
// 给Obj1的原型添加show()方法
Obj1.prototype.show = function () {
console.log('prototype.Obj1')
}
function Obj2() {
// 让Obj2继承Obj1的属性
Obj1.apply(this,arguments)
this.show = function () {
console.log('Obj2');
}
}
// 将Obj1的原型赋给Obj2的原型
Obj2.prototype = Obj1.prototype;
// 给Obj2的原型添加show()方法
Obj2.prototype.show = function () {
console.log('prototype.Obj2');
}
// 调用Obj2的属性
var Obj2 = new Obj2();
Obj2.show()
上面的代码我们可以看出Object、Obj1、Obj1.prototype、Obj2、Obj2.prototype都拥有show()方法,我们使Obj2继承Obj1的所有属性,并且将Obj1的原型赋给Obj2的原型,然后我们新建一个对象调用show()方法。那么问题来了,它到底会调用谁的方法呢?首先肯定是调用Obj2的方法,但是当我们把Obj2的方法注释掉的时候它会调用谁的呢,通过执行代码我们会发先,它执行的是Obj1的方法,如果我们再把Obj1的方法注释掉的时候它又会调用谁的呢?通过执行代码我们会发现show()方法调用的顺序是,先从Obj2开始,依次是Obj1,Obj2的原型,Obj1的原型,Object。
为避免发生以上问题,我们在命名属性名时应尽可能地避免属性名重复。
3、原型赋值会进行完全覆盖覆盖
如果将父类的原型赋值给子类的原型会发生,完全覆盖,如果想避免覆盖可以用for/in循环遍历父类的原型,来逐个添加。
// 构造函数Person
function Person(name, age){
this.name = name;
this.age = age;
this.show = function () {
console.log(this.name, this.age);
}
}
// 给Person的原型添加gender属性
Person.prototype.gender = "man";
// 构造函数
function Student(name, age, major) {
// 让Student继承Person的属性,将所有参数作为集合进行传递
Person.apply(this,arguments)
// 处理自己特殊的属性和方法
this.major = major;
this.show = function () {
console.log(this.name, this.age, this.major);
}
}
// 给Student的原型添加height(身高)属性
Student.prototype.height = 180;
// 将Person的原型赋给Student的原型
// Student.prototype = Person.prototype;
// 循环遍历Person的原型将其属性逐个赋给Student的原型
for ( key in Person.prototype) {
Student.prototype[key] = Person.prototype[key]
}
// 调用Student的属性
var student = new Student('xiaoming', 18, 'CAD');
console.log(student.height);
上述带码中我们先给Person的原型添加了gender属性,之后又给Student的原型添加了height属性,为了使Student也能继承gender的属性,我们将Person的原型赋给Student的原型。那么问题来了,此时我们再调用height属性时,显示的是undefined,为什么会发生这种情况呢?就是因为Person的原型将Student的原型完全覆盖了,想要解决这个问题很显然我们只需要改变一下代码顺序即可,但是如果我们就想要代码这么排列怎么办呢?此时我们就需要用到for/in循环,遍历Person的原型中的所有属性依次给Student的原型赋值即可解决完全覆盖的问题。代码执行效果如下:
四、给原始对象添加方法
1、Array
给数组添加addOne()方法,让所有调用此方法的数组中所有的元素全部加1。
// 给数组添加方法addOne()让数组中所有的元素全部加1
// 第一步:给数组原型添加方法
Array.prototype.addOne = function () {
// 第二步:声明数组,遍历数组中的元素
var newArray = [];
for (var i = 0; i < this.length; i++) {
newArray[i] = this[i] + 1;
}
// 第三步:返回新数组
return newArray;
};
var array = [134, 456, 89, 234, 568, 76543, 45678];
var newArray = array.addOne();
console.log(array);
console.log(newArray);
var age = [12, 34, 26, 78, 12];
var newAge = age.addOne();
console.log(age);
console.log(newAge);
// 给数组添加show()方法,调用此方法是打印该数组
Array.prototype.show = function () {
console.log(this);
};
age.show();
newAge.show();
2、String
给字符串添加trim()方法,使所有调用此方法的字符串,去掉首尾空格,中间多个空格改为一个空格。
// trim()首尾空格去掉,中间多个空格改为一个空格
//第一步:给string原型添加方法
String.prototype.trim = function () {
// 第二步:采用正则表达式进行替换
var res = this.replace(/^\s+|\s+$/g, "");
res = res.replace(/\s+/g, " ");
// 返回替换的值
return res;
}
var str = ' hsskfhsfu ewe eworew ';
var res = str.trim();
console.log("(" + str + ")");
console.log('('+res+')');
3、Date
给日期添加方法getWeek()获取当天是星期几。
// 给日期添加方法getWeek()获取当天是星期几
// 第一步:给Date原型添加方法
Date.prototype.getWeek = function () {
// 第二步:声明数组,保存所有结果
var array = ['星期天','星期一','星期二','星期三','星期四','星期五','星期六' ]
// 第三步:获取星期几的数字
var index = this.getDay();
// 第四步:根据星期几从数组中取出内容,并返回
return array[index]
}
var nowDate = new Date();
var res = nowDate.getWeek();
console.log(res);
五、instanceof
作用:判断某个对象是不是属于某个‘类’。
var arr = new Array();
var arr2 = [];
console.log(arr instanceof Array); // true
console.log(arr2 instanceof Array); // true
var str = new String();
var str2 = "";
console.log(str instanceof String); // true
console.log(str2 instanceof String); // false
function Person() {
}
var p = new Person();
console.log(p instanceof Person); // true
console.log(p instanceof Object); // true
console.log(Array instanceof Array); // false
console.log(String instanceof String); // false
console.log(Object instanceof Object); // true
console.log(Array instanceof Object); // true
var a = new Array;
console.log(a);
六、对象的克隆
1、类型:值类型、引用类型。
值类型:string(字符串),number(数字),boolean(布尔值)
引用类型:Array(数组),JSON,Object(对象),Date(时间日期对象)………
2、克隆引用类型
原理:
转为JSON字符串: var jsonStr = JSON.stringify(obj);
再从JSON字符串转化为对象: var obj = JSON.parse(jsonStr);
var a = 10;
var b = a;
b = 13456;
console.log(a);
var arr = [134, 45];
var arr2 = arr;
arr2[0] = 1234567890;
console.log(arr);
从上面的代码可以看出当我们把a赋给b时,我们改变变量b的值时,变量a的值不会发生变化;当我们用arr给arr2赋值时,当我们改变arr2的第一个元素的值时,数组arr的值也会随之发生改变。以下代码便可帮我们复制元素。
Object.prototype.copy = function () {
// console.log(this instanceof Array);
// 判断是否是数组
if (Array.isArray(this)) {
var jsonStr = JSON.stringify(this);
return JSON.parse(jsonStr);
} else { // other
// 创建新对象
var newObj = new Object();
// 把this中的每个属性和方法都赋值到新对象中
for (key in this) {
newObj[key] = this[key];
}
return newObj;
}
};
var arr = [134, 4567, 678];
var newArr = arr.copy();
// newArr[0] = 1000;
// console.log(arr);
// console.log(newArr);
var person1 = {
name: "haha",
age: 18,
test: function () {
console.log(this.age);
}
};
var newPerson = person1.copy();
newPerson.name = "heheheh";
newPerson.age = 19;
console.log(person1);
console.log(newPerson);
person1.test();
newPerson.test();
七、制作JQ插件
此处主要是给JQ原型或者$添加属性,因此需要用到以下两个属性:
$.extend({
funcName: function () {
}
}); 拓展给$
$.fn.extend({
funcName: function () {
}
}); 拓展给JQ
一般建议给JQ添加。
1、封装图片放大效果:
我们要做图片放大插件的效果就是,用户下载好插件的文件后,只需引入JQ和我们做好插件,并在html中放一张图片,然后在js中获取图片调用一个方法,再提供一大一小两张相同图片的路径作为参数即可实现,图片放大效果。
首先我们写一个模板做好html布局代码如下:
<body>
<div class="min-wrap">
<img src="images/min.jpg" alt="" />
<div id="slider"></div>
</div>
<div class="max-wrap">
<img src="images/max.jpg" alt="" />
</div>
</body>
布局完成后设置css样式,并将其单独存放在一个css文件中:
.min-wrap {
border: 1px solid black;
position: relative;
width: 300px;
left: 10px;
top: 20px;
}
.min-wrap img {
width: 100%;
}
#slider {
background: url(images/dian.png);
width: 150px;
height: 150px;
position: absolute;
left: 0;
top: 0;
display: none;
}
.max-wrap {
border: 1px solid black;
width: 400px;
height: 400px;
overflow: hidden;
position: absolute;
left: 330px;
top: 20px;
display: none;
}
然后我们开始写JS文件,我们要先获取head标签,在其中插入link标签引入我们写好的css文件,然后将我们写好的结构插入到用户提供的img标签后,使用我们写好的结构和样式,如此布局就算完成,之后我们按正常实现图片放大的效果写JS代码即可。
$.fn.extend({
fang: function (minImgPath, maxImgPath) {
// 引入样式文件
$('head').eq(0).append('<link rel="stylesheet" href="fang.css">');
// 新结构
var htmlStr = $('<div class="min-wrap">' +
'<img src="'+ minImgPath +'" alt="" />' +
'<div id="slider"></div>' +
'</div>' +
'<div class="max-wrap">' +
'<img src="'+ maxImgPath +'" alt="" />' +
'</div>');
// 拼接到image后面
this.after(htmlStr);
this.remove();
// 获取元素
var minWrap = $('.min-wrap');
var slider = $('#slider');
var maxWrap = $('.max-wrap');
var maxImg = $('.max-wrap img');
// 绑定事件
minWrap.on('mousemove', function (e) {
slider.show();
maxWrap.show();
var x = e.clientX - minWrap.offset().left - slider.width() / 2;
var y = e.clientY - minWrap.offset().top - slider.height() / 2;
// 判断临界值
if (x < 0) {
x = 0;
} else if (x > minWrap.width() - slider.width()) {
x = minWrap.width() - slider.width();
}
if (y < 0) {
y = 0;
} else if (y > minWrap.height() - slider.height()) {
y = minWrap.height() - slider.height();
}
// 移动slider
slider.css({
left: x + "px",
top: y + "px"
});
// 修改maxWrap的滚动
var scale = {
x: x / (minWrap.width() - slider.width()),
y: y / (minWrap.height() - slider.height())
};
var maxWrapLeft = (maxImg.width() - maxWrap.width()) * scale.x;
var maxWrapTop = (maxImg.height() - maxWrap.height()) * scale.y;
maxWrap.scrollLeft(maxWrapLeft);
maxWrap.scrollTop(maxWrapTop);
});
minWrap.on('mouseout', function () {
slider.hide();
maxWrap.hide();
});
}
});
用户使用的时候只需引入JQuery和我们写好的JS文件,然后使用fang()方法将大小两张图片的路径做为参数,即可实现效果。
八、练习
1、Tab切换
原生JS版代码
普通版Tap切换
// 普通版Tap切换
var inputs = document.getElementsByTagName('input');
var show = document.getElementsByClassName("show");
for (var i = 0; i < inputs.length; i++) {
inputs[i].index = i;
inputs[i].onclick = function () {
for (var j = 0; j < inputs.length; j++) {
show[j].style.display = 'none';
}
show[this.index].style.display = 'block';
}
}
面向对象版
// 面向对象班Tab切换
// 构造函数
function Tap () {
// 声明变量保存所有的div和input
this.inputs = document.getElementsByTagName("input");
this.divs = document.getElementsByClassName("show");
// 声明变量保存this
var mythis = this;
// 遍历所有按钮
for (var i = 0; i < this.inputs.length; i++) {
// 自定义按钮下标
this.inputs[i].index = i;
// 为所有按钮绑定事件
this.inputs[i].onclick = function () {
// 让Tab对象调用原型的方法将this作为参数进行传递
mythis.click(this)
}
}
}
// 为Tab的原型添加方法
Tap.prototype.click = function (obj) {
for (var i = 0; i < this.inputs.length; i++) {
this.divs[i].style.display = 'none';
}
this.divs[obj.index].style.display = 'block';
}
// 调用函数
var tab = new Tap();
JQuery版代码
普通版tab切换
// 普通版tab切换
$('input').click(function () {
$('.show').hide()
$('.show').eq($(this).index()).show()
})
面向对象版
// 面向对象班Tab切换
// 构造函数
function Tab() {
// 声明两个属性,保存所有的input和div
this.inputs = $('input');
this.divs = $('.show');
// 使第一个div处于显示的状态
this.divs.eq(0).show();
// 使用变量保存对象的this
var mythis = this;
// 给所有的按钮绑定事件,点击按钮时使对象调用原型的方法
this.inputs.click(function () {
mythis.myclick(this)
})
}
// 给Tab的原型添加方法
Tab.prototype.myclick = function (obj) {
this.divs.hide();
this.divs.eq($(obj).index()).show();
}
var tab = new Tab();
2、躁动的小球
原生JS普通版
// 获取元素
var box = document.getElementsByClassName('box')[0];
// 声明函数,生成随机整数
var randomFun = function (max, min) {
return parseInt(Math.random()*(max - min + 1) + min);
}
// 声明函数设置随机颜色
var colorFun = function () {
return 'rgba('+ randomFun(255,0) +','+ randomFun(255,0) +',' + randomFun(255,0)+ ',' + (Math.random() + 0.1) + ')';
}
// 循环创建小球
for (var i = 0; i < 500; i++) {
// 声明变量设置小球宽高
var w = randomFun(80, 20);
// 声明变量接收临界值
var maxY = document.body.offsetHeight - w;
var maxX = document.body.offsetWidth - w;
// 声明变量设置小球位置
var x = randomFun(maxX, 0);
var y = randomFun(maxY, 0);
// 创建小球并设置颜色
var ball = document.createElement("div");
ball.id = 'ball';
ball.style.top = y + 'px';
ball.style.left = x + 'px';
ball.style.width = w + 'px';
ball.style.height = w + 'px';
ball.style.backgroundColor = colorFun();
box.appendChild(ball)
}
面向对象版
// 一、获取元素
var box = document.getElementsByClassName('box')[0];
// 二、声明函数生成随机整数,用于设置小球宽高,还有top和left值
var randomFun = function (max, min) {
return parseInt(Math.random()*(max - min + 1) + min);
}
// 三、声明函数设置随机颜色
var colorFun = function () {
return 'rgba('+ randomFun(255,0) +','+ randomFun(255,0) +',' + randomFun(255,0)+ ',' + (Math.random() + 0.1) + ')';
}
// 四、获取屏幕的宽度和高度用于判断top和left的临界值
var maxY = document.body.offsetHeight;
var maxX = document.body.offsetWidth;
// 五、构造函数为小球添加宽高、坐标、颜色、父级等属性,并设置方法。
var Ball = function(x, y, h, w, color, parent) {
// 声明变量设置随机宽度
var wAndH = randomFun(80, 20);
// 为构造函数添加属性,如果有参数返回参数,否则返回随机x坐标。
this.x = x || randomFun(maxX-wAndH,0);
this.y = y || randomFun(maxY-wAndH,0);
this.h = h || wAndH;
this.w = w || wAndH;
this.color = color || colorFun();
this.parent = parent || box;
this.ball = document.createElement("div");
// 设置方法
this.show = function () {
this.ball.id = 'ball';
this.ball.style.width = this.w + 'px';
this.ball.style.height = this.h + 'px';
this.ball.style.top = this.y + 'px';
this.ball.style.left = this.x + 'px';
this.ball.style.backgroundColor = this.color;
this.parent.appendChild(this.ball);
}
}
// 六、循环创建对象,并调用该方法
for (var i = 0; i < 500; i++) {
var ball = new Ball();
ball.show();
}
以上内容纯属个人理解,由于水平有限,若有错漏之处敬请指出斧正,小弟不胜感激。