一、問題
(一)、繼承有什么作用?
通過繼承可以繼承原有函數的一些屬性和方法,避免重復定義一些屬性和方法,舉個例子:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="http://apps.bdimg.com/libs/jquery/1.9.1/jquery.min.js"></script>
</head>
<body>
<script>
function Person(name) {
this.name=name;
console.log(this.name)
}
Person.prototype.sayHi=function () {
console.log("Hi! my name is:"+this.name)
};
Person.prototype.sayEyesNum=function () {
console.log("I have two eyes.")
};
function Students(name) {
this.name=name;
Person.call(this,name)
}
Students.prototype=Object.create(Person.prototype); //這里一定要注意
//與Students.prototype.sayHi 間的順序,Students.prototype.sayHi應該在后面,否則其會被覆蓋掉!!
Students.prototype.constructor=Students;
Students.prototype.sayHi=function () {
console.log("Hi! I am student and my name is"+this.name)
};
Students.prototype.sayTeacherName=function () {
console.log("My Teacher's name is Wang")
};
var p1=new Person("ren"),
stu1=new Students("xue");
</script>
</body>
</html>
上述代碼中,我先定義了一個Person的函數,且在該函數屬性上綁定sayEyesNum()這個方法,然后我通過繼承的方法,使得Students函數也具有了sayEyesNum()這個方法,而無需重新在Students函數上綁定sayEyesNum()這個方法。
(二)、有幾種常見創建對象的方式? 舉例說明?
- 1、工廠模式:
function createPerson( name, age, job ) {
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function() {
alert(this.name);
}
return o;
}
var person1 = createPerson("Nicholas", 29, "software Engineer");
var person2 = createPerson("Greg", 27, "Doctor");
person1.sayName(); //"Nicholas"
person2.sayName(); //"Greg"
工廠模式未能夠解決對象識別的問題;
- 2、構造函數模式
function Person( name, age, job ) {
this.name = name;
this.age = age;
this.job = job;
this.sayName = function() {
alert(this.name);
}
}
var person1 = new Person("Nicholas", 29, "software Engineer");
var person2 = new Person("Greg", 27, "Doctor");
person1.sayName(); //"Nicholas"
person2.sayName(); //"Greg"
構造函數的主要問題是每個方法都會在每個實例上創建一遍。
- 3、原型模式
function Person(){
}
Person.prototype = {
constructor: Person,
name: "Nicholas",
age: 29,
job: "software Engineer",
friends: ["Shelby","Court"],
sayName: function() {
alert(this.name);
}
}
var person1 = new Person();
var person2 = new Person();
person1.friends.push("Van");
alert(person1.friends); //"Shelby,Court,Van"
alert(person2.friends); //"Shelby,Court,Van"
alert(person1.friends === person2.friends); //true
原型模式雖然解決了上述幾個問題,但是有時候我們需要某個實例要有自己的方法和屬性。因此出現了運用最廣泛的組合模式
- 4、組合使用構造函數模式和原型模式
function Person( name, age, job ) {
this.name = name;
this.age = age;
this.job = job;
this.friends = ["Shelby","Court"];
}
Person.prototype = {
constructor: Person,
sayName: function(){
alert(this.name);
}
}
var person1 = new Person("Nicholas", 29, "software Engineer");
var person2 = new Person("Greg", 27, "Doctor");
person1.friends.push("Van");
alert(person1.friends); //"Shelby,Court,Van"
alert(person2.friends); //"Shelby,Court"
alert(person1.friends === person2.friends); //false
alert(person1.sayName === person2.sayName); //true
該組合模式中,構造函數用于定義實例屬性,原型用于定義方法和共享屬性;
- 5、動態原型模式
function Person( name, age, job ) {
//屬性
this.name = name;
this.age = age;
this.job = job;
//方法
if( typeof this.sayName != "function" ) {
Person.prototype.sayName = function() {
alert(this.name);
}
}
}
var person = new Person("Nicholas", 29, "software Engineer");
person.sayName();
- 6、寄生構造函數模式
function SpecialArray() {
//創建數組
var values = new Array();
//添加值
values.push.apply(values,arguments);
//添加新方法
values.toPipedString = function(){
return this.join("|");
};
//返回數組
return values;
}
var colors = new SpecialArray("red","blue","green");
alert(colors.toPipedString()); //"red|blue|green"
返回的對象與構造函數或者與構造函數的原型之間沒有關系;不能依賴instanceof操作符來確定對象的類型。由于存在上述問題,我們建議在可以使用其他模式的情況下,不要使用這種模式。
- 7、穩妥構造函數模式
function Person( name, age, job ) {
//創建要返回的對象
var o = new Object();
//可以在這里定義私有變量和函數
//添加方法
o.sayName = function() {
alert(name);
};
//返回對象
return o;
}
var person = Person("Nicholas", 29, "software Engineer");
Person.sayName(); //"Nicholas"
與寄生構造函數模式類似,使用穩妥構造函數模式創建的對象與構造函數之間也沒什么關系, 因此instanceof操作符對這種對象沒有意義。
(三)、下面兩種寫法有什么區別?
//方法1
function People(name, sex){
this.name = name;
this.sex = sex;
this.printName = function(){
console.log(this.name);
}
}
var p1 = new People('饑人谷', 2)
//方法2
function Person(name, sex){
this.name = name;
this.sex = sex;
}
Person.prototype.printName = function(){
console.log(this.name);
}
var p1 = new Person('若愚', 27);
通過第一種方式,People的printName方法是在函數People實例對象里的,因此當后面再新建一個People的實例對象時,又會創建一個printName方法,而不能夠像第二種方式那樣可以在People函數的屬性上共用printName方法,不利于節省空間。
(四)、Object.create 有什么作用?兼容性如何?如何使用?
Object.create的作用是創建一個指定原型和若干個指定屬性的對象。
由于Object.create是在ES5后出現的,因此如下瀏覽器才支持:
它的語法如下:
Object.create(proto, [ propertiesObject ])
- proto 一個對象: 作為新創建的對象的原型;
- propertiesObject 可選。該參數對象是一組屬性與值,該對象的名稱將是新建對象的屬性名稱,值是描述性屬性符。值得注意的是該參數對象不能是undefined,另外該對象自身所擁有的可枚舉的屬性才有效,也就是說其原型鏈上的屬性是無效的。
舉個例子:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="http://apps.bdimg.com/libs/jquery/1.9.1/jquery.min.js"></script>
</head>
<body>
<script>
var o;
o = Object.create({}, { p: { value: 42 } });
// 省略了的屬性特性默認為false,所以屬性p是不可寫,不可枚舉,不可配置的:
o.p = 24;
o.p //42
o.A = 12;
for (var prop in o) {
console.log(prop)
}
//"A"
delete o.p;
//false
</script>
</body>
</html>
(五)、hasOwnProperty有什么作用? 如何使用?
用來判斷某個對象是否含有指定的自身屬性。其與in運算符不同,hasOwnProperty會忽略掉原型鏈上繼承到的屬性。
其使用語法為obj.hasOwnProperty(prop)
- prop 為要檢測的屬性名;
舉個例子:
<script>
var car={};
car.name="bench";
</script>
(六)、實現Object.create的 polyfill,如:(ps: 寫個 函數create,實現 Object.create 的功能)
var obj = {a: 1, b:2};
var obj2 = create(obj);
console.log(obj2.a); //1
簡單模擬:
function create(obj) {
function Tem() {}
Tem.prototype=obj;
return new Tem()
}
真實模擬:
if (typeof Object.create != 'function') {
// Production steps of ECMA-262, Edition 5, 15.2.3.5
// Reference: http://es5.github.io/#x15.2.3.5
Object.create = (function() {
//為了節省內存,使用一個共享的構造器
function Temp() {}
// 使用 Object.prototype.hasOwnProperty 更安全的引用
var hasOwn = Object.prototype.hasOwnProperty;
return function (O) {
// 1. 如果 O 不是 Object 或 null,拋出一個 TypeError 異常。
if (typeof O != 'object') {
throw TypeError('Object prototype may only be an Object or null');
}
// 2. 使創建的一個新的對象為 obj ,就和通過
// new Object() 表達式創建一個新對象一樣,
// Object是標準內置的構造器名
// 3. 設置 obj 的內部屬性 [[Prototype]] 為 O。
Temp.prototype = O;
var obj = new Temp();
Temp.prototype = null; // 不要保持一個 O 的雜散引用(a stray reference)...
// 4. 如果存在參數 Properties ,而不是 undefined ,
// 那么就把參數的自身屬性添加到 obj 上,就像調用
// 攜帶obj ,Properties兩個參數的標準內置函數
// Object.defineProperties() 一樣。
if (arguments.length > 1) {
// Object.defineProperties does ToObject on its first argument.
var Properties = Object(arguments[1]);
for (var prop in Properties) {
if (hasOwn.call(Properties, prop)) {
obj[prop] = Properties[prop];
}
}
}
// 5. 返回 obj
return obj;
};
})();
}
(七)、如下代碼中call的作用是什么?
function Person(name, sex){
this.name = name;
this.sex = sex;
}
function Male(name, sex, age){
Person.call(this, name, sex); //這里的 call 有什么作用
this.age = age;
}
這里call的作用是在函數Male的運行環境中運行Person函數,讓函數Male繼承了Person函數的屬性和方法;
(八)、補全代碼,實現繼承
function Person(name, sex){
// todo ...
}
Person.prototype.getName = function(){
// todo ...
};
function Male(name, sex, age){
//todo ...
}
//todo ...
Male.prototype.getAge = function(){
//todo ...
};
var ruoyu = new Male('若愚', '男', 27);
ruoyu.printName();
function Person(name, sex){
this.name=name;
this.sex=sex;
}
Person.prototype.getName = function(){
return (this.name)
};
function Male(name, sex, age){
this.name=name;
this.sex=sex;
this.age=age;
}
Person.prototype.printName=function () {
console.log(this.name)
};
Male.prototype=Object.create(Person.prototype);
Male.prototype.constructor=Male;
Male.prototype.getAge = function(){
return (this.age)
};
var ruoyu = new Male('若愚', '男', 27);
ruoyu.printName();
二、代碼
(一)、實現如下dialog 彈窗功能, 參考效果
//功能描述:
// 1. 可使用 dialog.open() 去打開彈窗
// 2. 當點擊確定、取消時可使用用戶自定義事件
// 3. dialog 可拖動
// 4. 允許頁面展示多個 dialog
function Dialog(){
//todo ...
}
var tpl = '<ul><li>列表1</li><li>列表2</li><li>列表1</li><li>列表1</li></ul>';
$('#open4').on('click',function(){
var dialog4 = new Dialog();
dialog4.open({
title: '歡迎來到饑人谷',
message: tpl,
isShowCloseBtn: true,
isShowConfirmBtn: true,
onClose: function(){
alert('close')
},
onConfirm: function(){
alert('確定');
}
});
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
body{
position: relative;
}
.dialog{
border-radius: 5px;
background-color:#eee ;
box-shadow:0 0 5px 2px rgba(0, 0, 0, 0.5);
position: absolute;
top: 30%;
left: 50%;
width: 328px;
}
.title{
background-color: #676666;
padding: 8px 10px;
color: #fff;
font-weight: bolder;
}
.clearfix:after{
display: block;
clear: both;
content: "";
}
a{
text-align: center;
text-decoration: none;
}
.button a{
background-color: #e33100;
padding: 5px 8px;
border-radius: 5px;
margin-left: 10px;
margin-right: 10px;
color: #fff;
}
.button{
padding: 20px 0;
text-align: center;
}
.title a{
float: right;
width: 10px;
color: #fff;
}
.content{
color:#666 ;
padding: 10px;
}
.draggable{
cursor: move;
opacity: 0.8;
}
</style>
<script type="text/javascript" src="http://apps.bdimg.com/libs/jquery/1.9.1/jquery.min.js"></script>
</head>
<body>
<button id="open1">打開1</button>
<button id="open2">打開2</button>
<button id="open3">打開3</button>
<button id="open4">打開4</button>
<button id="open5">打開5</button>
<p>鼠標在 dialog 上左鍵按下可拖動 dialog</p>
<p>可創建多個 dialog</p>
<script>
//功能描述:
// 1. 可使用 dialog.open() 去打開彈窗
// 2. 當點擊確定、取消時可使用用戶自定義事件
// 3. dialog 可拖動
// 4. 允許頁面展示多個 dialog
function Dialog(){
this.createDialog();
this.bindEvent();
}
Dialog.prototype={
originOpts:{
title:"",
message:"",
isShowCancelBtn:true,
isShowConfirmBtn:false,
onCancel:function () {
},
onConfirm:function () {
}
},
open:function (opts) {
this.setOpts(opts);
this.setDialog();
this.showDialog();
},
setOpts:function (opts) {
if (typeof opts==="string"){
this.opts=$.extend({},this.originOpts,{message:opts})
}else if (typeof opts==="object"){
this.opts=$.extend({},this.originOpts,opts)
}
},
setDialog:function () {
var $dialog=this.$dialog;
console.log($dialog);
if (!this.opts.title){
$dialog.find(".title").hide();
}else {
$dialog.find(".title").show();
}
if (!this.opts.isShowCancelBtn){
$dialog.find(".button .cancel").hide();
}else {
$dialog.find(".button .cancel").show();
}
if (!this.opts.isShowConfirmBtn){
$dialog.find(".confirm").hide();
}else {
$dialog.find(".confirm").show();
}
$dialog.find(".title span").text(this.opts.title);
$dialog.find(".content").html(this.opts.message);
},
showDialog:function () {
this.$dialog.show();
},
hideDialog:function () {
this.$dialog.hide();
},
createDialog:function () {
var tp1='<div class="dialog">'+'<div class="title clearfix"><span></span><a class="cancel" href="####">X</a></div>'+'<h3 class="content"></h3>'+'<div class="button"><a class="cancel" href="####">取消</a><a class="confirm" href="####">確定</a></div>'+'</div>';
this.$dialog=$(tp1);
$("body").append(this.$dialog);
},
bindEvent:function () {
var _this=this;
_this.$dialog.find(".cancel").on("click",function () {
_this.opts.onCancel();
_this.hideDialog();
});
_this.$dialog.find(".confirm").on("click",function () {
_this.opts.onConfirm();
_this.hideDialog();
});
_this.$dialog.on("mousedown",function (e) {
var $dialog=$(this),
evtX=e.pageX-$dialog.offset().left,
evtY=e.pageY-$dialog.offset().top;
$dialog.addClass("draggable").data("evtPos",{
x:evtX,
y:evtY
})
});
$("body").on("mousemove",function (e) {
$(".draggable").length && $(".draggable").offset({
top:e.pageY-$(".draggable").data("evtPos").y,
left:e.pageX-$(".draggable").data("evtPos").x
});
$("body").on("mouseup",function () {
$('.draggable').length && $('.draggable').removeClass('draggable').removeData('evtPos');
})
})
}
};
$('#open1').on('click', function() {
var dialog1 = new Dialog();
dialog1.open('hello, 這里是饑人谷');
});
$('#open2').on('click', function() {
var dialog2 = new Dialog();
dialog2.open('<a href="####">這里是鏈接</a>');
});
$('#open3').on('click',function(){
var dialog3 = new Dialog();
dialog3.open({
title: '歡迎來到饑人谷',
message: "hello",
isShowCancelBtn: true,
isShowConfirmBtn: true,
onCancel: function(){
alert('cancel')
},
onConfirm: function(){
alert('確定');
}
});
});
var tp1 = '<ul><li>列表1</li><li>列表2</li><li>列表1</li><li>列表1</li></ul>';
$('#open4').on('click',function(){
var dialog4 = new Dialog();
dialog4.open({
title: '歡迎來到饑人谷',
message: tp1,
isShowCancelBtn: true,
isShowConfirmBtn: true,
onCancel: function(){
alert('cancel')
},
onConfirm: function(){
alert('確定');
}
});
});
$('#open5').on('click',function(){
var dialog5 = new Dialog();
dialog5.open({
title: '歡迎來到饑人谷',
message: "hello",
isShowCancelBtn: false,
isShowConfirmBtn: false
});
});
</script>
</body>
</html>
(二)、實現如下一個日歷組件 【Demo】
<input class="date-ipt" type="text" placeholder="有初始值" date-init="2016/05/31" />
<input class="date-ipt" type="text" placeholder="無初始值" />
<script>
// 使用
$('.date-ipt').datePicker();
</script>
**本文版權歸本人即簡書筆名:該賬戶已被查封 所有,如需轉載請注明出處。謝謝! *